home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ms_sh23s.zip / SRC / SH1.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  78KB  |  3,763 lines

  1. /*
  2.  * MS-DOS SHELL - Main program, memory and variable management
  3.  *
  4.  * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth
  5.  *
  6.  * This code is based on (in part) the shell program written by Charles
  7.  * Forsyth and is subject to the following copyright restrictions:
  8.  *
  9.  * 1.  Redistribution and use in source and binary forms are permitted
  10.  *     provided that the above copyright notice is duplicated in the
  11.  *     source form and the copyright notice in file sh6.c is displayed
  12.  *     on entry to the program.
  13.  *
  14.  * 2.  The sources (or parts thereof) or objects generated from the sources
  15.  *     (or parts of sources) cannot be sold under any circumstances.
  16.  *
  17.  * When parts of the original 2.1 shell were replaced by the Lexical
  18.  * Analsyer written by Simon J. Gerraty (for his Public Domain Korn Shell,
  19.  * which is also based on Charles Forsyth original idea), a number of changes
  20.  * were made to reflect the changes Simon made to the Parse output tree.  Some
  21.  * parts of this code in this module are based on the algorithms/ideas that
  22.  * he incorporated into his shell, in particular interfaces to the new Lexical
  23.  * Scanner.
  24.  *
  25.  *    $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh1.c,v 2.19 1994/08/25 20:49:11 istewart Exp $
  26.  *
  27.  *    $Log: sh1.c,v $
  28.  *    Revision 2.19  1994/08/25  20:49:11  istewart
  29.  *    MS Shell 2.3 Release
  30.  *
  31.  *    Revision 2.18  1994/02/23  09:23:38  istewart
  32.  *    Beta 233 updates
  33.  *
  34.  *    Revision 2.17  1994/02/01  10:25:20  istewart
  35.  *    Release 2.3 Beta 2, including first NT port
  36.  *
  37.  *    Revision 2.16  1994/01/20  14:51:43  istewart
  38.  *    Release 2.3 Beta 1
  39.  *
  40.  *    Revision 2.15  1994/01/11  17:55:25  istewart
  41.  *    Release 2.3 Beta 0 patches
  42.  *
  43.  *    Revision 2.14  1993/12/01  11:58:34  istewart
  44.  *    Release 226 beta
  45.  *
  46.  *    Revision 2.13  1993/11/09  10:39:49  istewart
  47.  *    Beta 226 checking
  48.  *
  49.  *    Revision 2.12  1993/08/25  16:03:57  istewart
  50.  *    Beta 225 - see Notes file
  51.  *
  52.  *    Revision 2.11  1993/07/02  10:21:35  istewart
  53.  *    224 Beta fixes
  54.  *
  55.  *    Revision 2.10  1993/06/16  12:55:49  istewart
  56.  *    Fix the -s and -t options so they work
  57.  *
  58.  *    Revision 2.9  1993/06/14  11:00:12  istewart
  59.  *    More changes for 223 beta
  60.  *
  61.  *    Revision 2.8  1993/06/02  09:52:35  istewart
  62.  *    Beta 223 Updates - see Notes file
  63.  *
  64.  *    Revision 2.7  1993/02/16  16:03:15  istewart
  65.  *    Beta 2.22 Release
  66.  *
  67.  *    Revision 2.6  1993/01/26  18:35:09  istewart
  68.  *    Release 2.2 beta 0
  69.  *
  70.  *    Revision 2.5  1992/12/14  10:54:56  istewart
  71.  *    BETA 215 Fixes and 2.1 Release
  72.  *
  73.  *    Revision 2.4  1992/11/06  10:03:44  istewart
  74.  *    214 Beta test updates
  75.  *
  76.  *    Revision 2.3  1992/09/03  18:54:45  istewart
  77.  *    Beta 213 Updates
  78.  *
  79.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  80.  *    Beta 212 Baseline
  81.  *
  82.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  83.  *    211 Beta updates
  84.  *
  85.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  86.  *    MS-Shell 2.0 Baseline release
  87.  */
  88.  
  89. #include <sys/types.h>
  90. #include <sys/stat.h>
  91. #include <stdio.h>
  92. #include <stdlib.h>
  93. #include <signal.h>
  94. #include <errno.h>
  95. #include <setjmp.h>
  96. #include <stdarg.h>
  97. #include <string.h>
  98. #include <unistd.h>
  99. #include <ctype.h>
  100. #include <fcntl.h>
  101. #include <limits.h>
  102. #include <dirent.h>
  103. #include <time.h>
  104. #include "sh.h"
  105.  
  106. /* OS2 or DOS, DEBUG MEMORY or normal malloc */
  107.  
  108. #ifdef OS2_DOSALLOC
  109. #  ifdef DEBUG_MEMORY
  110. #    define RELEASE_MEMORY(x)    if (DosFreeSeg (SELECTOROF ((x))))    \
  111.                     fprintf(stderr, "Memory Release error\n");
  112. #  else
  113. #    define RELEASE_MEMORY(x)    DosFreeSeg (SELECTOROF ((x)))
  114. #  endif
  115.  
  116. #else
  117. #  define RELEASE_MEMORY(x)     free ((x))
  118. #endif
  119.  
  120. /*
  121.  * Structure of Malloced space to allow release of space nolonger required
  122.  * without having to know about it.
  123.  */
  124.  
  125. typedef struct region {
  126.     struct region    *next;
  127.     size_t        nbytes;
  128.     int            area;
  129. } s_region;
  130.  
  131. static struct region    *MemoryAreaHeader = (s_region *)NULL;
  132.  
  133. /*
  134.  * default shell, search rules
  135.  */
  136.  
  137. #if (OS_TYPE == OS_UNIX)
  138. static char        *shellname = "/bin/pksh";
  139. static char        search[] = ":/bin:/usr/bin";
  140.  
  141. extern char        **environ;
  142. #else
  143. static char        *shellname = "c:/bin/sh.exe";
  144. static char        search[] = ";c:/bin;c:/usr/bin";
  145. #endif
  146. static char        *ymail = "You have mail";
  147. static char        *ShellNameLiteral = "sh";
  148. static char        *ShellOptions = "D:MPRX:abc:defghijklmnopqrtsuvwxyz0";
  149.  
  150. #if (OS_TYPE != OS_DOS)
  151. static char        DefaultWinTitle[] = "MS Shell";
  152. #endif
  153.  
  154. #if (OS_TYPE == OS_OS2)
  155. #  if (OS_SIZE == OS_32)
  156. static HEV        SessionQueueSema = 0;
  157. static HQUEUE        SessionQueueHandler = 0;
  158. #  else
  159. static HSEM        SessionQueueSema = 0;
  160. static HQUEUE        SessionQueueHandler = 0;
  161. #  endif
  162. #endif
  163.  
  164. static char        *tilde = "~";
  165. static char        LIT_OSmode[] = "OSMODE";
  166. static char        LIT_SHmode[] = "SHMODE";
  167. static char        LIT_Dollar[] = "$";
  168. #if (OS_TYPE == OS_DOS)
  169. static char    *NOExit = "sh: ignoring attempt to leave lowest level shell";
  170. #endif
  171. static bool        ProcessingDEBUGTrap = FALSE;
  172. static bool        ProcessingERRORTrap = FALSE;
  173. static unsigned int    ATOE_GFlags;    /* Twalk GLOBAL flags        */
  174. static unsigned int    ATNE_Function;    /* Twalk GLOBAL flags        */
  175.  
  176. /*
  177.  * Count value for counting the number entries in an array
  178.  */
  179.  
  180. static int        Count_Array;    /* Array size            */
  181. static char        *Count_Name;    /* Array name            */
  182.  
  183. /* Integer Variables */
  184.  
  185. static struct ShellVariablesInit {
  186.     char    *Name;            /* Name of variable        */
  187.     int        Status;            /* Initial status        */
  188.     char    *CValue;
  189. } InitialiseShellVariables[] = {
  190.     { LIT_COLUMNS,        STATUS_INTEGER,        "80"        },
  191.     { HistorySizeVariable,    STATUS_INTEGER,        "100"        },
  192.     { LineCountVariable,    STATUS_INTEGER,        "1"        },
  193.     { LIT_LINES,        STATUS_INTEGER,        "25"        },
  194.     { OptIndVariable,        STATUS_INTEGER,        "1"        },
  195.     { StatusVariable,        STATUS_INTEGER,        "0"        },
  196.     { RandomVariable,        (STATUS_EXPORT | STATUS_INTEGER),
  197.                             null        },
  198.     { SecondsVariable,        (STATUS_EXPORT | STATUS_INTEGER),
  199.                             null        },
  200.     { LIT_OSmode,        (STATUS_EXPORT | STATUS_CANNOT_UNSET |
  201.                  STATUS_INTEGER),    null        },
  202.     { LIT_SHmode,        (STATUS_EXPORT | STATUS_CANNOT_UNSET |
  203.                  STATUS_INTEGER),    null        },
  204.     { LIT_Dollar,        (STATUS_CANNOT_UNSET | STATUS_INTEGER),
  205.                             null        },
  206.  
  207.     { LastWordVariable,        STATUS_EXPORT,        null        },
  208.     { PathLiteral,        (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  209.                             search        },
  210.     { IFS,            (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  211.                             " \t\n"        },
  212.     { PS1,            (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  213.                             "%e$ "        },
  214.     { PS2,            (STATUS_EXPORT | STATUS_CANNOT_UNSET),
  215.                             "> "        },
  216.     { PS3,            STATUS_EXPORT,        "#? "        },
  217.     { PS4,            STATUS_EXPORT,        "+ "        },
  218.     { HomeVariableName,        STATUS_EXPORT,        null        },
  219.     { ShellVariableName,    STATUS_EXPORT,        null        },
  220.  
  221. #if (OS_TYPE != OS_DOS)
  222.     { WinTitleVariable,     0,            DefaultWinTitle    },
  223. #endif
  224.  
  225.     { (char *)NULL,        0 }
  226. };
  227.  
  228. /*
  229.  * List of Editor variables
  230.  */
  231.  
  232. #if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
  233. static char        *EditorVariables[] = {
  234.     FCEditVariable, EditorVariable, VisualVariable, (char *)NULL
  235. };
  236. #endif
  237.  
  238.                     /* Entry directory        */
  239. static char    Start_directory[PATH_MAX + 6] = { 0 };
  240. static time_t    ShellStartTime = 0;    /* Load time of shell        */
  241.                     /* Original Interrupt 24 address */
  242. #if (OS_TYPE == OS_DOS) && !defined (__EMX__)
  243. #if defined (__WATCOMC__)
  244. #    define INTERRUPT_TYPE        __interrupt __far
  245. #  else
  246. #    define INTERRUPT_TYPE        interrupt far
  247. #  endif
  248. static void    (INTERRUPT_TYPE *Orig_I24_V) (void);
  249. #endif
  250.  
  251. #ifdef SIGQUIT
  252. static void    (*qflag)(int) = SIG_IGN;
  253. #endif
  254.  
  255. /* Functions */
  256.  
  257. static int F_LOCAL    RunCommands (Source *);
  258. static unsigned char * F_LOCAL    CheckClassExpression (unsigned char *, int,
  259.                               bool);
  260. static bool F_LOCAL    Initialise (int, char **);
  261. static void F_LOCAL    CheckForMailArriving (void);
  262. static void F_LOCAL    LoadGlobalVariableList (void);
  263. static void F_LOCAL    LoadTheProfileFiles (void);
  264. static void F_LOCAL    ConvertUnixPathToMSDOS (char *);
  265. static void F_LOCAL    ClearUserPrompts (void);
  266. static void F_LOCAL    SecondAndRandomEV (char *, long);
  267. static void F_LOCAL    SetUpParameterEV (int, char **, char *);
  268. static bool F_LOCAL    AllowedToSetVariable (VariableList *);
  269. static void F_LOCAL    SetUpANumericValue (VariableList *, long, int);
  270. static void F_LOCAL    ProcessErrorExit (char *, va_list);
  271. static char * F_LOCAL    SuppressSpacesZeros (VariableList *, char *);
  272. static void        AddToNewEnvironment (const void *, VISIT, int);
  273. static void        AddToOldEnvironment (const void *, VISIT, int);
  274. static void        DeleteEnvironment (const void *, VISIT, int);
  275. static void F_LOCAL    CheckOPTIND (char *, long);
  276. static void F_LOCAL    CreateIntegerVariables (void);
  277. static bool F_LOCAL    ExecuteShellScript (char *);
  278. static void         CountEnvironment (const void *, VISIT, int);
  279.  
  280. /*
  281.  * Process termination
  282.  */
  283.  
  284. #if (OS_TYPE != OS_DOS)
  285. static void F_LOCAL    CheckForTerminatedProcess (void);
  286.  
  287. #  if (OS_TYPE == OS_NT)
  288. static void        LookUpJobs (const void *, VISIT, int);
  289. #  endif
  290.  
  291. #else
  292. #  define CheckForTerminatedProcess()
  293. #endif
  294.  
  295. /*
  296.  * No real argv interface
  297.  */
  298.  
  299. #if (OS_TYPE == OS_UNIX)
  300. #define Pre_Process_Argv(a,b)
  301. #else
  302. static void F_LOCAL    Pre_Process_Argv (char **, int *);
  303. #endif
  304.  
  305. /*
  306.  * OS/2 Session Queues
  307.  */
  308.  
  309. #if (OS_TYPE == OS_OS2)
  310. static void F_LOCAL    CheckForSessionEnd (void);
  311. static void F_LOCAL    CreateTerminationQueues (void);
  312. #else
  313. #  define CheckForSessionEnd()
  314. #  define CreateTerminationQueues()
  315. #endif
  316.  
  317. #if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
  318. static void F_LOCAL    SetEditorMode (char *);
  319. #endif
  320.  
  321. /*
  322.  * The Program Name and Command Line, set up by stdargv.c
  323.  */
  324.  
  325. #if (OS_TYPE != OS_UNIX)
  326. extern char         *_APgmName;
  327. extern char        *_ACmdLine;
  328. #endif
  329.  
  330. /*
  331.  * The main program starts here
  332.  */
  333.  
  334. void main (int argc, char **argv)
  335. {
  336.     int            cflag = 0;
  337.     int            sc;
  338.     char        *name = *argv;
  339.                     /* Load up various parts of the    */
  340.                     /* system            */
  341.     bool        OptionsRflag = Initialise (argc, argv);
  342.     bool        OptionsXflag = FALSE;    /* -x option from    */
  343.                         /* command line        */
  344.     bool        Level0Shell = FALSE;    /* Level 0 (read profile)*/
  345.     jmp_buf        ReturnPoint;
  346.     char        *cp;
  347.     Source        *s;
  348.     int            fid;
  349.     bool        TTYInput;
  350.     bool        PIPEInput;
  351.     bool        RootShell = FALSE;
  352.  
  353.     SetWindowName ((char *)NULL);
  354.  
  355. /* Create Parse input */
  356.  
  357.     s = pushs (SFILE);
  358.     s->u.file = stdin;
  359.  
  360. /* Set up start time */
  361.  
  362.     ShellStartTime = time ((time_t *)NULL);
  363.  
  364. /* Preprocess options to convert two character options of the form /x to
  365.  * -x.  Some programs!!
  366.  */
  367.  
  368.     if (argc > 1)
  369.     Pre_Process_Argv (argv, &argc);
  370.  
  371. /* Save the start directory for when we exit */
  372.  
  373.     S_getcwd (Start_directory, 0);
  374.  
  375. /* Process the options */
  376.  
  377.     while ((sc = GetOptions (argc, argv, ShellOptions, GETOPT_MESSAGE)) != EOF)
  378.     {
  379.     switch (sc)
  380.     {
  381.         case '?':
  382.         FinalExitCleanUp (1);
  383.  
  384.         case '0':                /* Level 0 flag for DOS    */
  385.         Level0Shell = TRUE;
  386.         break;
  387.  
  388.         case 'r':                /* Restricted        */
  389.         OptionsRflag = TRUE;
  390.         break;
  391.  
  392.         case 'c':                /* Command on line    */
  393.         ClearUserPrompts ();
  394.         cflag = 1;
  395.         s->type = SSTRING;
  396.         s->str = OptionArgument;
  397.         SetVariableFromString ("_cString", OptionArgument);
  398.         break;
  399.  
  400.         case 'q':                /* No quit ints        */
  401. #ifdef SIGQUIT
  402.         qflag = SIG_DFL;
  403. #endif
  404.         break;
  405.  
  406.  
  407.         case 'X':
  408.         if (!GotoDirectory (OptionArgument, GetCurrentDrive ()))
  409.         {
  410.             PrintErrorMessage ("%s: bad directory", OptionArgument);
  411.             FinalExitCleanUp (1);
  412.         }
  413.  
  414.         break;
  415.  
  416.         case 'x':
  417.         OptionsXflag = TRUE;
  418.         break;
  419.  
  420.         case 'M':
  421.         ShellGlobalFlags |= FLAGS_MSDOS_FORMAT;
  422.             break;
  423.  
  424.         case 'D':
  425.         AssignVariableFromString (OptionArgument, (int *)NULL);
  426.             break;
  427.  
  428.         case 'R':
  429.         RootShell = TRUE;
  430.         ChangeInitLoad = TRUE;    /* Change load .ini pt.        */
  431.         break;
  432.  
  433. #if (OS_TYPE != OS_DOS)
  434.         case 'P':                /* Use real pipes    */
  435.             ShellGlobalFlags |= FLAGS_REALPIPES;
  436.             break;
  437. #endif
  438.  
  439.         case 's':                /* standard input    */
  440.             if (cflag)
  441.             PrintErrorMessage ("cannot use -s and -c together");
  442.  
  443.         case 'i':                /* Set interactive    */
  444.         InteractiveFlag = TRUE;
  445.  
  446.         default:
  447.         if (islower (sc))
  448.             FL_SET (sc);
  449.     }
  450.  
  451. /* If -s, set the argv to point to -s so that the rest of the parameters
  452.  * get used as parameters ($digit) values.
  453.  */
  454.  
  455.         if (FL_TEST (FLAG_POSITION))
  456.     {
  457.         OptionIndex--;
  458.         break;
  459.     }
  460.     }
  461.  
  462. /* Under UNIX, check for login shell */
  463.  
  464. #if (OS_TYPE == OS_UNIX)
  465.     if (*argv[0] == '-')
  466.     Level0Shell = TRUE;
  467. #endif
  468.  
  469.     argv += OptionIndex;
  470.     argc -= OptionIndex;
  471.  
  472. /* Get configuration info */
  473.  
  474.     Configure_Keys ();
  475.  
  476. /*
  477.  * Check for terminal input.  A special case for OS/2.  If pipe input and
  478.  * output and no arguments, set interactive flag.
  479.  *
  480.  * Unset the variable after!
  481.  */
  482.  
  483.     TTYInput  = C2bool ((IS_TTY (0) && IS_TTY (1)));
  484.     PIPEInput = C2bool ((IS_Pipe (0) && IS_Pipe (1) &&
  485.              (GetVariableAsString (LIT_AllowTTY, FALSE) != null)));
  486.  
  487.     UnSetVariable (LIT_AllowTTY, -1, TRUE);
  488.  
  489.     if ((s->type == SFILE) && 
  490.     ((FL_TEST (FLAG_INTERACTIVE)) || TTYInput || PIPEInput) &&
  491.     !cflag &&
  492.     ((argc == 0) || FL_TEST (FLAG_POSITION)))
  493.     {
  494.     s->type = STTY;
  495.     FL_SET (FLAG_POSITION);
  496.     FL_SET (FLAG_INTERACTIVE);
  497.     InteractiveFlag = TRUE;
  498.  
  499.     if  (TTYInput)
  500.         PrintVersionNumber (stderr);
  501.     }
  502.  
  503. /* Root shell - check only tty devices */
  504.  
  505.     if (RootShell)
  506.     {
  507.     if ((s->type != STTY) || !TTYInput)
  508.         PrintErrorMessage ("-R not valid on non-interactive shells");
  509.  
  510. #if (OS_TYPE == OS_DOS) && !defined (__EMX__)
  511.     Orig_I24_V = (void (INTERRUPT_TYPE *)())NULL;
  512. #endif
  513.     }
  514.  
  515. /*
  516.  * Execute commands from a file? - disable prompts
  517.  */
  518.  
  519.     if ((s->type == SFILE) && (argc > 0) && !InteractiveFlag)
  520.     {
  521.     ClearUserPrompts ();
  522.     name = *argv;
  523.  
  524.     if (((fid = S_open (FALSE, s->file = name, O_RMASK)) < 0) ||
  525.         ((s->u.file = ReOpenFile (ReMapIOHandler (fid), sOpenReadMode))
  526.                     == (FILE *)NULL))
  527.     {
  528.         PrintErrorMessage (LIT_Emsg, "cannot open script", name,
  529.                    strerror (errno));
  530.         FinalExitCleanUp (1);
  531.     }
  532.  
  533. /* Un-map this file descriptor from the current environment so it does not
  534.  * get closed at the wrong times
  535.  */
  536.     ChangeFileDescriptorStatus (fileno (s->u.file), FALSE);
  537.  
  538.  
  539. #if (OS_TYPE != OS_DOS) 
  540.     SetVariableFromString (WinTitleVariable, name);
  541. #endif
  542.     }
  543.  
  544. /* Setup stderr, stdout buffering */
  545.  
  546.    setvbuf (stderr, (char *)NULL, _IONBF, 0);
  547.  
  548.    if (InteractiveFlag && !IS_TTY (1))
  549.        setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);
  550.  
  551. /* Set up the $- variable */
  552.  
  553.     SetShellSwitches ();
  554.  
  555. #ifdef SIGQUIT
  556.     signal (SIGQUIT, qflag);
  557. #endif
  558.  
  559. /* Set up signals */
  560.  
  561.     if (InteractiveFlag)
  562.     signal (SIGTERM, TerminateSignalled);
  563.  
  564. /* Load any parameters */
  565.  
  566.     SetUpParameterEV (argc, argv, name);
  567.  
  568. /* Return point */
  569.  
  570.     if (SetErrorPoint (ReturnPoint))
  571.     ExitTheShell (FALSE);
  572.  
  573.     signal (SIGINT, InterruptSignalled);
  574.  
  575. /* Read profile ?.  Init EMAC first for binding keys */
  576.  
  577.     if (((name != (char *)NULL) && (*name == CHAR_HYPHEN)) || Level0Shell)
  578.     {
  579. #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
  580.     EMACS_Initialisation ();
  581. #  endif
  582.     LoadTheProfileFiles ();
  583.     }
  584.  
  585. /*
  586.  * Load history and configure
  587.  */
  588.  
  589.     if (s->type == STTY)
  590.     {
  591.     HistoryEnabled = TRUE;
  592.     LoadHistory ();
  593.     }
  594.  
  595. /*
  596.  * If the x or r flag was set on the command line, set it now after the
  597.  * profiles have been executed.
  598.  */
  599.  
  600.     if (OptionsXflag)
  601.     FL_SET (FLAG_PRINT_EXECUTE);
  602.  
  603.     if (OptionsRflag)
  604.     {
  605.     FL_SET (FLAG_READONLY_SHELL);
  606.     RestrictedShellFlag = TRUE;
  607.     }
  608.  
  609. /*
  610.  * Execute $ENV
  611.  *
  612.  * TOCHECK - substitute (cp, DOTILDE);
  613.  */
  614.  
  615.     if ((cp = GetVariableAsString (ENVVariable, FALSE)) != null)
  616.     ExecuteShellScript (cp);
  617.  
  618. /* If interactive, set up Editor modes */
  619.  
  620. #if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
  621.     if (InteractiveFlag)
  622.     {
  623.     char        **rep = EditorVariables;
  624.  
  625.     while (*rep != (char *)NULL)
  626.     {
  627.         if ((cp = GetVariableAsString (*(rep++), FALSE)) != null)
  628.         {
  629.         SetEditorMode (cp);
  630.         break;
  631.         }
  632.     }
  633.     }
  634. #endif
  635.  
  636. /*
  637.  * Execute what ever we have to do!!
  638.  */
  639.  
  640.     while (TRUE)
  641.     {
  642.     switch (SetErrorPoint (ReturnPoint))
  643.     {
  644.         case TERMINATE_POINT_SET:
  645.         RunCommands (s);        /* Drop to exit        */
  646.  
  647.         case TERMINATE_COMMAND:
  648.         default:
  649.         ExitTheShell (FALSE);
  650.  
  651. /* Re-set TTY input.  If we reach this point, the shell is a root shell and
  652.  * the no exit message has been displayed.  Reset the shell for input from the
  653.  * TTY.
  654.  */
  655.  
  656.         case TERMINATE_SHELL:
  657.         s->type = STTY;
  658.         break;
  659.     }
  660.     }
  661. }
  662.  
  663. /*
  664.  * Process a script file
  665.  */
  666.  
  667. static bool F_LOCAL ExecuteShellScript (char *name)
  668. {
  669.     FILE    *f = stdin;
  670.     int        fp;
  671.     Source    *s;
  672.  
  673.     if (strcmp (name, "-") != 0)
  674.     {
  675.     if ((fp = OpenForExecution (name, (char **)NULL, (int *)NULL)) < 0)
  676.         return FALSE;
  677.  
  678.     if ((f = ReOpenFile (fp = ReMapIOHandler (fp),
  679.                  sOpenReadMode)) == (FILE *)NULL)
  680.         return FALSE;
  681.     }
  682.  
  683.     (s = pushs (SFILE))->u.file = f;
  684.     s->file = name;
  685.  
  686.     RunCommands (s);
  687.  
  688.     if (f != stdin)
  689.     CloseFile (f);
  690.  
  691.     return TRUE;
  692. }
  693.  
  694. /*
  695.  * run the commands from the input source, returning status.
  696.  */
  697.  
  698. static int F_LOCAL RunCommands (Source *src)
  699. {
  700.     int        i;
  701.     jmp_buf    ReturnPoint;
  702.     C_Op    *t = (C_Op *)NULL;
  703.     bool    wastty;
  704.     int        EntryMemoryLevel = MemoryAreaLevel + 1;
  705.  
  706.     CreateNewEnvironment ();
  707.     e.ErrorReturnPoint = (ErrorPoint)NULL;
  708.     e.line = GetAllocatedSpace (LINE_MAX);
  709.  
  710. /*
  711.  * Run until the end
  712.  */
  713.  
  714.     while (TRUE)
  715.     {
  716.  
  717. /* Initialise space */
  718.  
  719.     MemoryAreaLevel = EntryMemoryLevel;
  720.     ReleaseMemoryArea (MemoryAreaLevel);
  721.     SW_intr = 0;
  722.     ProcessingEXECCommand = FALSE;
  723.  
  724.     if (src->next == NULL)
  725.         src->echo = C2bool (FL_TEST (FLAG_ECHO_INPUT));
  726.  
  727. /*
  728.  * Set up a few things for console input - cursor, mail prompt etc
  729.  */
  730.  
  731.     if ((wastty = C2bool (src->type == STTY)))
  732.     {
  733.         PositionCursorInColumnZero ();
  734.         CheckForMailArriving ();
  735.         CheckForTerminatedProcess ();
  736.         CloseAllHandlers ();    /* Clean up any open shell files */
  737.     }
  738.  
  739.     LastUserPrompt = PS1;
  740.     FlushStreams ();            /* Clear output */
  741.  
  742. /* Set execute function recursive level and the SubShell count to zero */
  743.  
  744.     Execute_stack_depth = 0;
  745.  
  746. /* Set up Redirection IO (Saved) array and SubShell Environment information */
  747.  
  748.     NSave_IO_E = 0;        /* Number of entries        */
  749.     MSave_IO_E = 0;        /* Max Number of entries    */
  750.     NSubShells = 0;        /* Number of entries        */
  751.     MSubShells = 0;        /* Max Number of entries    */
  752.     CurrentFunction = (FunctionList *)NULL;
  753.     CurrentFunction = (FunctionList *)NULL;
  754.     ProcessingDEBUGTrap = FALSE;
  755.     ProcessingERRORTrap = FALSE;
  756.     Break_List = (Break_C *)NULL;
  757.     Return_List = (Break_C *)NULL;
  758.     SShell_List = (Break_C *)NULL;
  759.     ProcessingEXECCommand = FALSE;
  760.  
  761. /* Get the line and process it */
  762.  
  763.     if (SetErrorPoint (ReturnPoint) ||
  764.         ((t = BuildParseTree (src)) == (C_Op *)NULL) || SW_intr)
  765.     {
  766.         ScrapHereList ();
  767.  
  768.         if ((!InteractiveFlag && SW_intr) || FL_TEST (FLAG_ONE_COMMAND))
  769.         ExitTheShell (FALSE);
  770.  
  771. /* Go round again */
  772.  
  773.         src->str = null;
  774.         SW_intr = 0;
  775.         continue;
  776.     }
  777.  
  778. /* Ok - reset some variables and then execute the command tree */
  779.  
  780.     SW_intr = 0;
  781.     ProcessingEXECCommand = FALSE;
  782.     FlushHistoryBuffer ();        /* Save history            */
  783.  
  784. /* Check for exit */
  785.  
  786.     if ((t != NULL) && (t->type == TEOF))
  787.     {
  788.         if (wastty && (ShellGlobalFlags & FLAGS_IGNOREEOF))
  789.         {
  790.         PrintWarningMessage ("Use `exit'");
  791.         src->type = STTY;
  792.         continue;
  793.         }
  794.  
  795.         else
  796.         break;
  797.     }
  798.  
  799. /* Execute the parse tree */
  800.  
  801.     if ((SetErrorPoint (ReturnPoint) == 0) &&
  802.         ((!FL_TEST (FLAG_NO_EXECUTE)) || (src->type == STTY)))
  803.         ExecuteParseTree (t, NOPIPE, NOPIPE, 0);
  804.  
  805. /* Make sure the I/O and environment are back at level 0 and then clear them */
  806.  
  807.     e.ErrorReturnPoint = (ErrorPoint)NULL;
  808.     Execute_stack_depth = 0;
  809.     ClearExtendedLineFile ();
  810.  
  811.     if (NSubShells != 0)
  812.         DeleteGlobalVariableList ();
  813.  
  814.     if (NSave_IO_E)
  815.         RestoreStandardIO (0, TRUE);
  816.  
  817.     if (MSubShells)
  818.         ReleaseMemoryCell ((void *)SubShells);
  819.  
  820.     if (MSave_IO_E)
  821.         ReleaseMemoryCell ((void *)SSave_IO);
  822.  
  823.     /* Check for interrupts */
  824.  
  825.     if ((!InteractiveFlag && SW_intr) || FL_TEST (FLAG_ONE_COMMAND))
  826.     {
  827.         ProcessingEXECCommand = FALSE;
  828.         ExitTheShell (FALSE);
  829.     }
  830.  
  831. /* Run any traps that are required */
  832.  
  833.     if ((i = InterruptTrapPending) != 0)
  834.     {
  835.         InterruptTrapPending = 0;
  836.         RunTrapCommand (i);
  837.     }
  838.     }
  839.  
  840. /*
  841.  * Terminate the current environment
  842.  */
  843.  
  844.     QuitCurrentEnvironment ();
  845.     return ExitStatus;
  846. }
  847.  
  848. /*
  849.  * Set up the value of $-
  850.  */
  851.  
  852. void SetShellSwitches (void)
  853. {
  854.     char    *cp, c;
  855.     char    m['z' - 'a' + 2];
  856.  
  857.     for (cp = m, c = 'a'; c <= 'z'; ++c)
  858.     {
  859.     if (FL_TEST (c))
  860.         *(cp++) = c;
  861.     }
  862.  
  863.     if (ShellGlobalFlags & FLAGS_MSDOS_FORMAT)
  864.     *(cp++) = 'M';
  865.  
  866.     *cp = 0;
  867.     SetVariableFromString (ShellOptionsVariable, m);
  868. }
  869.  
  870. /*
  871.  * Terminate current environment with an error
  872.  */
  873.  
  874. void TerminateCurrentEnvironment (int TValue)
  875. {
  876.     FlushStreams ();            /* Clear output */
  877.  
  878.     if (e.ErrorReturnPoint != (ErrorPoint)NULL)
  879.     ExitErrorPoint (TValue);
  880.  
  881.     /* NOTREACHED */
  882. }
  883.  
  884. /*
  885.  * Exit the shell
  886.  */
  887.  
  888. void ExitTheShell (bool ReturnRequired)
  889. {
  890.     FlushStreams ();            /* Clear output */
  891.  
  892.     if (ProcessingEXECCommand)
  893.     TerminateCurrentEnvironment (TERMINATE_COMMAND);
  894.  
  895. #if (OS_TYPE == OS_DOS) && !defined (__EMX__)
  896.     if (Orig_I24_V == (void (INTERRUPT_TYPE *)())NULL)
  897.     {
  898.     feputs (NOExit);
  899.  
  900.     if (!ReturnRequired)
  901.         TerminateCurrentEnvironment (TERMINATE_SHELL);
  902.     }
  903. #endif
  904.  
  905. /* Clean up */
  906.  
  907.     ScrapHereList ();
  908.     FreeAllHereDocuments (1);
  909.  
  910. /* Trap zero on exit */
  911.  
  912.     RunTrapCommand (0);
  913.  
  914. /* Dump history on exit */
  915.  
  916.     DumpHistory ();
  917.  
  918.     CloseAllHandlers ();
  919.  
  920. /* Clear swap file if necessary */
  921.  
  922.     ClearSwapFile ();
  923.  
  924. /* If this is a command only - restore the directory because DOS doesn't
  925.  * and the user might expect it
  926.  */
  927.  
  928.     if (*Start_directory)
  929.     RestoreCurrentDirectory (Start_directory);
  930.  
  931. /* If this happens during startup - we restart */
  932.  
  933. #if (OS_TYPE == OS_DOS) && !defined (__EMX__)
  934.     if (Orig_I24_V == (void (INTERRUPT_TYPE *)())NULL)
  935.     return;
  936. #endif
  937.  
  938. /*
  939.  * Clean up any Here Document files left in the function tree
  940.  */
  941.  
  942.     DeleteAllFunctions ();
  943.  
  944. /* Exit - hurray */
  945.  
  946.     FinalExitCleanUp (ExitStatus);
  947.  
  948. /* NOTREACHED */
  949. }
  950.  
  951. /*
  952.  * Output warning message
  953.  */
  954.  
  955. int PrintWarningMessage (char *fmt, ...)
  956. {
  957.     va_list    ap;
  958.  
  959.     va_start (ap, fmt);
  960.     vfprintf (stderr, fmt, ap);
  961.     feputc (CHAR_NEW_LINE);
  962.     ExitStatus = -1;
  963.  
  964. /* If leave on error - exit */
  965.  
  966.     if (FL_TEST (FLAG_EXIT_ON_ERROR))
  967.     ExitTheShell (FALSE);
  968.  
  969.     va_end (ap);
  970.     return 1;
  971. }
  972.  
  973. /*
  974.  * Shell error message
  975.  */
  976.  
  977. void ShellErrorMessage (char *fmt, ...)
  978. {
  979.     va_list    ap;
  980.  
  981. /* Error message processing */
  982.  
  983.     if (source->file == (char *)NULL)
  984.     feputs ("sh: ");
  985.  
  986.     else
  987.     fprintf (stderr, "%s: at line %d, ", source->file, source->line);
  988.  
  989.     va_start (ap, fmt);
  990.     ProcessErrorExit (fmt, ap);
  991.     va_end (ap);
  992. }
  993.  
  994. /*
  995.  * Output error message
  996.  */
  997.  
  998. void PrintErrorMessage (char *fmt, ...)
  999. {
  1000.     va_list    ap;
  1001.  
  1002. /* Error message processing */
  1003.  
  1004.     va_start (ap, fmt);
  1005.     ProcessErrorExit (fmt, ap);
  1006.     va_end (ap);
  1007. }
  1008.  
  1009. /*
  1010.  * Common processing for PrintErrorMessage and ShellError Message
  1011.  */
  1012.  
  1013. static void F_LOCAL ProcessErrorExit (char *fmt, va_list ap)
  1014. {
  1015.     vfprintf (stderr, fmt, ap);
  1016.     feputc (CHAR_NEW_LINE);
  1017.  
  1018.     ExitStatus = -1;
  1019.  
  1020.     if (FL_TEST (FLAG_EXIT_ON_ERROR))
  1021.     ExitTheShell (FALSE);
  1022.  
  1023. /* Error processing */
  1024.  
  1025.     if (FL_TEST (FLAG_NO_EXECUTE))
  1026.     return;
  1027.  
  1028. /* If not interactive - exit */
  1029.  
  1030.     if (!InteractiveFlag)
  1031.     ExitTheShell (FALSE);
  1032.  
  1033.     if (e.ErrorReturnPoint != (ErrorPoint)NULL)
  1034.     ExitErrorPoint (TERMINATE_COMMAND);
  1035.  
  1036. /* CloseAllHandlers (); Removed - caused problems.  There may be problems
  1037.  * remaining with files left open?
  1038.  */
  1039. }
  1040.  
  1041. /*
  1042.  * Create or delete a new environment.  If f is set, delete the environment
  1043.  */
  1044.  
  1045. void CreateNewEnvironment (void)
  1046. {
  1047.     ShellFileEnvironment    *ep;
  1048.  
  1049. /* Create a new environment */
  1050.  
  1051.     if ((ep = (ShellFileEnvironment *)
  1052.         GetAllocatedSpace (sizeof (ShellFileEnvironment)))
  1053.         == (ShellFileEnvironment *)NULL)
  1054.     {
  1055.     while (e.PreviousEnvironment)
  1056.         QuitCurrentEnvironment ();
  1057.  
  1058.     TerminateCurrentEnvironment (TERMINATE_COMMAND);
  1059.     }
  1060.  
  1061.     *ep = e;
  1062.     e.PreviousEnvironment = ep;
  1063.     e.IOMap = 0L;
  1064.     e.OpenStreams = (Word_B *)NULL;
  1065. }
  1066.  
  1067. /*
  1068.  * Exit the current environment successfully
  1069.  */
  1070.  
  1071. void QuitCurrentEnvironment (void)
  1072. {
  1073.     ShellFileEnvironment    *ep;
  1074.     unsigned long        FdMap;
  1075.     Word_B            *wb;
  1076.     int                NEntries;
  1077.     int                i;
  1078.  
  1079. /* Restore old environment, delete the space and close any files opened in
  1080.  * this environment
  1081.  */
  1082.  
  1083.     if ((ep = e.PreviousEnvironment) != (ShellFileEnvironment *)NULL)
  1084.     {
  1085.  
  1086. /* Close opened streams */
  1087.  
  1088.     wb = e.OpenStreams;
  1089.     NEntries = WordBlockSize (wb);
  1090.  
  1091.     for (i = 0; i < NEntries; i++)
  1092.     {
  1093.         if (wb->w_words[i] != (char *)NULL)
  1094.         fclose ((FILE *)wb->w_words[i]);
  1095.     }
  1096.  
  1097. /* Get the files used in this environment to close */
  1098.  
  1099.     FdMap = e.IOMap;
  1100.     e = *ep;
  1101.  
  1102.     ReleaseMemoryCell ((void *)ep);
  1103.  
  1104.     for (i = 0; i < 32; i++)
  1105.     {
  1106.         if (FdMap & (1L << i))
  1107.         S_close (i + FDBASE, TRUE);
  1108.     }
  1109.     }
  1110. }
  1111.  
  1112. /*
  1113.  * Convert binary to ascii
  1114.  */
  1115.  
  1116. char *IntegerToString (int n)
  1117. {
  1118.     static char        nt[10];
  1119.  
  1120.     sprintf (nt, "%u", n);
  1121.     return nt;
  1122. }
  1123.  
  1124. /*
  1125.  * SIGINT interrupt processing
  1126.  */
  1127.  
  1128. void InterruptSignalled (int signo)
  1129. {
  1130. /* Restore signal processing and set SIGINT detected flag */
  1131.  
  1132.     signal (signo, InterruptSignalled);
  1133.  
  1134. #if SIGNALDEBUG
  1135.     fprintf (stderr, "Interrupt %d detected\n", signo); fflush (stderr);
  1136. #endif
  1137.  
  1138. /* Under OS/2, if the Ignore Interrupts flag is set, ignore them.  To do
  1139.  * with starting OS/2 programs in sh3.c
  1140.  */
  1141.  
  1142. #if (OS_TYPE != OS_DOS)
  1143.     if (IgnoreInterrupts)
  1144.         return;
  1145. #endif
  1146.  
  1147. /* Set interrupt detected */
  1148.  
  1149.     SW_intr = 1;
  1150.  
  1151. /* Zap the swap file, just in case it got corrupted */
  1152.  
  1153.     ClearSwapFile ();
  1154.  
  1155. /* Are we talking to the user?  Yes - Abandon processing */
  1156.  
  1157.     if (InteractiveFlag)
  1158.     TerminateCurrentEnvironment (TERMINATE_COMMAND);
  1159.  
  1160. /* No - exit */
  1161.  
  1162.     else
  1163.     {
  1164.     ProcessingEXECCommand = FALSE;
  1165.     ExitStatus = 1;
  1166.     ExitTheShell (FALSE);
  1167.     }
  1168. }
  1169.  
  1170. /*
  1171.  * Grap some space and check for an error
  1172.  */
  1173.  
  1174. void *GetAllocatedSpace (size_t n)
  1175. {
  1176.     void    *cp;
  1177.  
  1178.     if ((cp = AllocateMemoryCell (n)) == (void *)NULL)
  1179.     PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);
  1180.  
  1181.     return cp;
  1182. }
  1183.  
  1184. /*
  1185.  * Re-allocate some space
  1186.  */
  1187.  
  1188. void    *ReAllocateSpace (void *OldSpace, size_t NewSize)
  1189. {
  1190.     void    *NewSpace;
  1191.  
  1192.     if ((NewSpace = GetAllocatedSpace (NewSize)) == (void *)NULL)
  1193.         return NewSpace;
  1194.  
  1195.     if (OldSpace != (void *)NULL)
  1196.     {
  1197.     size_t    OldSize = ((s_region *)((char *)OldSpace -
  1198.                     sizeof (s_region)))->nbytes;
  1199.  
  1200.     SetMemoryAreaNumber (NewSpace, GetMemoryAreaNumber (OldSpace));
  1201.     memcpy (NewSpace, OldSpace, OldSize);
  1202.     ReleaseMemoryCell (OldSpace);
  1203.     }
  1204.  
  1205.     return NewSpace;
  1206. }
  1207.  
  1208.  
  1209. /*
  1210.  * Duplicate a memory Area
  1211.  */
  1212.  
  1213. void        *DuplicateMemoryCell (void *cell)
  1214. {
  1215.     void    *new;
  1216.     size_t    len = ((s_region *)((char *)cell - sizeof (s_region)))->nbytes;
  1217.  
  1218.     if ((new = AllocateMemoryCell (len)) == (void *)NULL)
  1219.     PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);
  1220.  
  1221.     else
  1222.     memcpy (new, cell, len);
  1223.  
  1224.     return new;
  1225. }
  1226.  
  1227.  
  1228. /*
  1229.  * Get memory Area size
  1230.  */
  1231.  
  1232. size_t        GetMemoryCellSize (void *cell)
  1233. {
  1234.     return ((s_region *)((char *)cell - sizeof (s_region)))->nbytes;
  1235. }
  1236.  
  1237. /*
  1238.  * Save a string in a given area
  1239.  */
  1240.  
  1241. char *StringSave (char *s)
  1242. {
  1243.     char    *cp;
  1244.  
  1245.     if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
  1246.     {
  1247.     SetMemoryAreaNumber ((void *)cp, 0);
  1248.     return strcpy (cp, s);
  1249.     }
  1250.  
  1251.     return null;
  1252. }
  1253.  
  1254. /*
  1255.  * Duplicate at current Memory level
  1256.  */
  1257.  
  1258. char *StringCopy (char *s)
  1259. {
  1260.     char    *cp;
  1261.  
  1262.     if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
  1263.     return strcpy (cp, s);
  1264.  
  1265.     return null;
  1266. }
  1267.  
  1268. /*
  1269.  * trap handling - Save signal number and restore signal processing
  1270.  */
  1271.  
  1272. void TerminateSignalled (int i)
  1273. {
  1274.     if (i == SIGINT)        /* Need this because swapper sets it    */
  1275.     {
  1276.     SW_intr = 0;
  1277.  
  1278. /* Zap the swap file, just in case it got corrupted */
  1279.  
  1280.     ClearSwapFile ();
  1281.     }
  1282.  
  1283.     InterruptTrapPending = i;
  1284.     signal (i, TerminateSignalled);
  1285. }
  1286.  
  1287. /*
  1288.  * Execute a trap command
  1289.  *
  1290.  *  0 - exit trap
  1291.  * -1 - Debug Trap
  1292.  * -2 - Error Trap
  1293.  */
  1294.  
  1295. void RunTrapCommand (int i)
  1296. {
  1297.     Source    *s;
  1298.     char    *trapstr;
  1299.     char    tval[10];
  1300.     char    *tvalp = tval;
  1301.  
  1302. /* Check for special values and recursion */
  1303.  
  1304.     if (i == -1)
  1305.     {
  1306.     tvalp = Trap_DEBUG;
  1307.  
  1308.     if (ProcessingDEBUGTrap)
  1309.         return;
  1310.  
  1311.     ProcessingDEBUGTrap = TRUE;
  1312.     }
  1313.  
  1314. /* Error trap */
  1315.  
  1316.     else if (i == -2)
  1317.     {
  1318.     tvalp = Trap_ERR;
  1319.  
  1320.     if (ProcessingERRORTrap)
  1321.         return;
  1322.  
  1323.     ProcessingERRORTrap = TRUE;
  1324.     }
  1325.  
  1326.     else
  1327.     sprintf (tval, "~%d", i);
  1328.  
  1329.     if ((trapstr = GetVariableAsString (tvalp, FALSE)) == null)
  1330.     return;
  1331.  
  1332. /* If signal zero, save a copy of the trap value and then delete the trap */
  1333.  
  1334.     if (i == 0)
  1335.     {
  1336.     trapstr = StringCopy (trapstr);
  1337.     UnSetVariable (tval, -1, TRUE);
  1338.     }
  1339.  
  1340.     (s = pushs (SSTRING))->str = trapstr;
  1341.     RunACommand (s, (char **)NULL);
  1342.  
  1343.     ProcessingDEBUGTrap = FALSE;
  1344.     ProcessingERRORTrap = FALSE;
  1345. }
  1346.  
  1347. /*
  1348.  * Find the given name in the dictionary and return its value.  If the name was
  1349.  * not previously there, enter it now and return a null value.
  1350.  */
  1351.  
  1352. VariableList    *LookUpVariable (char *name,    /* Variable name    */
  1353.                  int  Index,    /* Array Index        */
  1354.                  bool cflag)    /* Create flag        */
  1355. {
  1356.     VariableList        *vp;
  1357.     VariableList        **vpp;
  1358.     int                c;
  1359.     static VariableList        dummy;
  1360.     void            (*save_signal)(int);
  1361.  
  1362. /* Set up the dummy variable */
  1363.  
  1364.     memset (&dummy, 0, sizeof (VariableList));
  1365.     dummy.name = name;
  1366.     dummy.status = STATUS_READONLY;
  1367.     dummy.value = null;
  1368.  
  1369. /* If digit string - use the dummy to return the value */
  1370.  
  1371.     if (isdigit (*name))
  1372.     {
  1373.     for (c = 0; isdigit (*name) && (c < 1000); name++)
  1374.         c = c * 10 + *name - '0';
  1375.  
  1376.     c += Index;
  1377.     dummy.value = (c <= ParameterCount) ? ParameterArray[c] : null;
  1378.     return &dummy;
  1379.     }
  1380.  
  1381. /* Look up in list */
  1382.  
  1383.     dummy.index = Index;
  1384.     vpp = (VariableList **)tfind (&dummy, &VariableTree, SearchVariable);
  1385.  
  1386. /* If we found it, return it */
  1387.  
  1388.     if (vpp != (VariableList **)NULL)
  1389.     {
  1390.         vp = *vpp;
  1391.  
  1392. /* Special processing for SECONDS and RANDOM */
  1393.  
  1394.     if (!strcmp (name, SecondsVariable) &&
  1395.         !(DisabledVariables & DISABLE_SECONDS))
  1396.         SetUpANumericValue (vp, time ((time_t *)NULL) - ShellStartTime, 10);
  1397.  
  1398.     else if (!strcmp (name, RandomVariable) &&
  1399.          !(DisabledVariables & DISABLE_RANDOM))
  1400.         SetUpANumericValue (vp, (long)rand(), 10);
  1401.  
  1402.     return vp;
  1403.     }
  1404.  
  1405. /* If we don't want to create it, return a dummy */
  1406.  
  1407.     dummy.status |= STATUS_NOEXISTANT;
  1408.  
  1409.     if (!cflag)
  1410.     return &dummy;
  1411.  
  1412. /* Create a new variable.  If no memory, use the dummy */
  1413.  
  1414.     dummy.name = null;
  1415.  
  1416.     if ((vp = (VariableList *)GetAllocatedSpace (sizeof (VariableList)))
  1417.         == (VariableList *)NULL)
  1418.     return &dummy;
  1419.  
  1420.     if ((vp->name = StringCopy (name)) == null)
  1421.     {
  1422.     ReleaseMemoryCell ((void *)vp);
  1423.     return &dummy;
  1424.     }
  1425.  
  1426. /* Set values */
  1427.  
  1428.     vp->value = null;
  1429.     vp->index = Index;
  1430.     vp->status = NSubShells ? 0 : STATUS_GLOBAL;
  1431.  
  1432. /* Save signals */
  1433.  
  1434.     save_signal = signal (SIGINT, SIG_IGN);
  1435.  
  1436. /* Add to the tree */
  1437.  
  1438.     if (tsearch (vp, &VariableTree, SearchVariable) == (void *)NULL)
  1439.     {
  1440.     ReleaseMemoryCell ((void *)vp->name);
  1441.     ReleaseMemoryCell ((void *)vp);
  1442.         vp = &dummy;
  1443.     }
  1444.  
  1445. /* OK Added OK - set up memory */
  1446.  
  1447.     else
  1448.     {
  1449.     SetMemoryAreaNumber ((void *)vp, 0);
  1450.     SetMemoryAreaNumber ((void *)vp->name, 0);
  1451.     }
  1452.  
  1453. /* Restore signals */
  1454.  
  1455.     signal (SIGINT, save_signal);
  1456.  
  1457.     return vp;
  1458. }
  1459.  
  1460. /*
  1461.  * Count the number of entries in an Array Variable
  1462.  */
  1463.  
  1464. int    CountVariableArraySize (char *name)
  1465. {
  1466.     if (isdigit (*name))
  1467.     return ParameterCount;
  1468.  
  1469.     Count_Array = 0;
  1470.     Count_Name = name;
  1471.     twalk (VariableTree, CountEnvironment);
  1472.     return Count_Array;
  1473. }
  1474.  
  1475. /*
  1476.  * TWALK - Count the Environment Variables in an array
  1477.  */
  1478.  
  1479. static void CountEnvironment (const void *key, VISIT visit, int level)
  1480. {
  1481.     if (((visit == postorder) || (visit == leaf)) &&
  1482.        (strcmp (Count_Name, (*(VariableList **)key)->name) == 0))
  1483.     Count_Array++;
  1484. }
  1485.  
  1486. /*
  1487.  * TFIND & TSEARCH - Search the VARIABLE TREE for an entry
  1488.  */
  1489.  
  1490. int    SearchVariable (const void *key1, const void *key2)
  1491. {
  1492.     int            diff;
  1493.  
  1494.     if ((diff = strcmp (((VariableList *)key1)->name,
  1495.             ((VariableList *)key2)->name)) != 0)
  1496.     return diff;
  1497.  
  1498.     return ((VariableList *)key1)->index - ((VariableList *)key2)->index;
  1499. }
  1500.  
  1501. /*
  1502.  * Execute an assignment.  If a valid assignment, load it into the variable
  1503.  * list.
  1504.  *
  1505.  * If value is not NULL, assign it.  Otherwise don't
  1506.  */
  1507.  
  1508. bool    AssignVariableFromString (char *String,    /* The assignment string */
  1509.                   int  *Index)    /* Index value returned    */
  1510. {
  1511.     char    *cp;
  1512.     long    value = 0;
  1513.  
  1514. /* Ignore if not valid environment variable - check alpha and equals */
  1515.  
  1516.     if (!GetVariableName (String, &value, &cp, (bool*)NULL) ||
  1517.     (*cp != CHAR_ASSIGN))
  1518.     {
  1519.     if (value == -1)
  1520.         PrintErrorMessage (LIT_BadArray, String);
  1521.  
  1522.     return FALSE;
  1523.     }
  1524.  
  1525. /* Change the = to a end of string */
  1526.  
  1527.     *(cp++) = 0;
  1528.  
  1529. /* Assign the value */
  1530.  
  1531.     SetVariableArrayFromString (String, (int)value, cp);
  1532.  
  1533. /* Return the index */
  1534.  
  1535.     if (Index != (int *)NULL)
  1536.     *Index = (int)value;
  1537.  
  1538.     return TRUE;
  1539. }
  1540.  
  1541. /*
  1542.  * Get variable name and index
  1543.  *
  1544.  * String either ends in a null or assignment
  1545.  */
  1546.  
  1547. bool    GetVariableName (char *String,    /* The original string        */
  1548.              long *Index,    /* Array index value found    */
  1549.              char **Value,    /* Pointer to the value        */
  1550.              bool *Array)    /* Array detected flag        */
  1551. {
  1552.     char    *cp, *sp;
  1553.     char    EndName;
  1554.  
  1555.     *Index = 0;
  1556.  
  1557. /* Ignore if not valid environment variable - check alpha and equals */
  1558.  
  1559.     if (((EndName = IsValidVariableName (String)) != CHAR_ASSIGN) &&
  1560.     (EndName != CHAR_OPEN_BRACKETS) && EndName)
  1561.     return FALSE;
  1562.  
  1563.     if ((cp = strchr (String, CHAR_ASSIGN)) == (char *)NULL)
  1564.     cp = &String[strlen (String)];
  1565.  
  1566.     if (Array != (bool *)NULL)
  1567.     *Array = C2bool (EndName == CHAR_OPEN_BRACKETS);
  1568.  
  1569. /* Check for valid array */
  1570.  
  1571.     if (EndName == CHAR_OPEN_BRACKETS)
  1572.     {
  1573.     if ((Index == (long *)NULL) || (*(cp - 1) != CHAR_CLOSE_BRACKETS))
  1574.         return FALSE;
  1575.  
  1576. /* Terminate the name and remove the trailing bracket */
  1577.  
  1578.     *(sp = strchr (String, CHAR_OPEN_BRACKETS)) = 0;
  1579.     *(cp - 1) = 0;
  1580.  
  1581.     if ((!ConvertNumericValue (sp + 1, Index, 10)) ||
  1582.         (*Index < 0) || (*Index > INT_MAX))
  1583.     {
  1584.         *Index = -1;
  1585.         return FALSE;
  1586.     }
  1587.     }
  1588.  
  1589. /* Return pointer to null or assignment */
  1590.  
  1591.     *Value = cp;
  1592.     return TRUE;
  1593. }
  1594. /*
  1595.  * Duplicate the Variable List for a Subshell
  1596.  *
  1597.  * Create a new Var_list environment for a Sub Shell
  1598.  */
  1599.  
  1600. int CreateGlobalVariableList (unsigned int Function)
  1601. {
  1602.     int            i;
  1603.     S_SubShell        *sp;
  1604.  
  1605.     for (sp = SubShells, i = 0; (i < NSubShells) &&
  1606.                    (SubShells[i].depth < Execute_stack_depth);
  1607.      i++);
  1608.  
  1609. /* If depth is greater or equal to the Execute_stack_depth - we should panic
  1610.  * as this should not happen.  However, for the moment, I'll ignore it
  1611.  */
  1612.  
  1613.     if (NSubShells == MSubShells)
  1614.     {
  1615.     sp = (S_SubShell *)ReAllocateSpace ((MSubShells == 0) ? (void *)NULL
  1616.                                   : SubShells,
  1617.                         (MSubShells + SSAVE_IO_SIZE) *
  1618.                         sizeof (S_SubShell));
  1619. /* Check for error */
  1620.  
  1621.     if (sp == (S_SubShell *)NULL)
  1622.         return -1;
  1623.  
  1624.     SetMemoryAreaNumber ((void *)sp, 0);
  1625.     SubShells = sp;
  1626.     MSubShells += SSAVE_IO_SIZE;
  1627.     }
  1628.  
  1629. /* Save the depth and the old Variable Tree value */
  1630.  
  1631.     sp = &SubShells[NSubShells++];
  1632.     sp->OldVariableTree = VariableTree;
  1633.     sp->depth  = Execute_stack_depth;
  1634.     sp->GFlags = ShellGlobalFlags | Function;
  1635.     sp->Eflags = flags;
  1636.     VariableTree = (void *)NULL;
  1637.  
  1638. /* Duplicate the old Variable list */
  1639.  
  1640.     ATNE_Function = Function;
  1641.     twalk (sp->OldVariableTree, AddToNewEnvironment);
  1642.  
  1643. /* Reset global values */
  1644.  
  1645.     LoadGlobalVariableList ();
  1646.     return 0;
  1647. }
  1648.  
  1649. /*
  1650.  * TWALK - add to new environment
  1651.  */
  1652.  
  1653. static void AddToNewEnvironment (const void *key, VISIT visit, int level)
  1654. {
  1655.     VariableList    *vp = *(VariableList **)key;
  1656.     VariableList    *vp1;
  1657.  
  1658.     if ((visit == postorder) || (visit == leaf))
  1659.     {
  1660.  
  1661. /* For functions, do not copy the traps */
  1662.  
  1663.     if (ATNE_Function && (*vp->name == CHAR_TILDE) && vp->name[1])
  1664.         return;
  1665.  
  1666. /* Create a new entry */
  1667.  
  1668.     vp1 = LookUpVariable (vp->name, vp->index, TRUE);
  1669.  
  1670.     if ((!(vp->status & STATUS_INTEGER)) && (vp->value != null))
  1671.         vp1->value = StringSave (vp->value);
  1672.  
  1673. /* Copy some flags */
  1674.  
  1675.     vp1->status = vp->status;
  1676.     vp1->nvalue = vp->nvalue;
  1677.     vp1->base = vp->base;
  1678.     vp1->width = vp->width;
  1679.     }
  1680. }
  1681.  
  1682. /*
  1683.  * Delete a SubShell environment and restore the original
  1684.  */
  1685.  
  1686. void DeleteGlobalVariableList (void)
  1687. {
  1688.     int            j;
  1689.     S_SubShell        *sp;
  1690.     VariableList    *vp;
  1691.     void        (*save_signal)(int);
  1692.  
  1693.     for (j = NSubShells; j > 0; j--)
  1694.     {
  1695.        sp = &SubShells[j - 1];
  1696.  
  1697.        if (sp->depth < Execute_stack_depth)
  1698.        break;
  1699.  
  1700. /* Reduce number of entries */
  1701.  
  1702.     --NSubShells;
  1703.  
  1704. /* Disable signals */
  1705.  
  1706.     save_signal = signal (SIGINT, SIG_IGN);
  1707.  
  1708. /* Restore the previous level information */
  1709.  
  1710.     vp = VariableTree;
  1711.     VariableTree = sp->OldVariableTree;
  1712.     ShellGlobalFlags = (unsigned int)(sp->GFlags & ~FLAGS_FUNCTION);
  1713.     flags = sp->Eflags;
  1714.  
  1715. /* Release the space */
  1716.  
  1717.     ATOE_GFlags = sp->GFlags;
  1718.  
  1719.     twalk (vp, AddToOldEnvironment);
  1720.     twalk (vp, DeleteEnvironment);
  1721.  
  1722. /* Restore signals */
  1723.  
  1724.     signal (SIGINT, save_signal);
  1725.  
  1726.     LoadGlobalVariableList ();
  1727.     }
  1728. }
  1729.  
  1730. /*
  1731.  * TWALK - delete old environment tree
  1732.  */
  1733.  
  1734. static void DeleteEnvironment (const void *key, VISIT visit, int level)
  1735. {
  1736.     VariableList    *vp = *(VariableList **)key;
  1737.  
  1738.     if ((visit == endorder) || (visit == leaf))
  1739.     {
  1740.         if (vp->value == null)
  1741.             ReleaseMemoryCell ((void *)vp->value);
  1742.  
  1743.         ReleaseMemoryCell ((void *)vp->name);
  1744.         ReleaseMemoryCell ((void *)vp);
  1745.     }
  1746. }
  1747.  
  1748. /*
  1749.  * TWALK - Transfer Current Environment to the Old one
  1750.  */
  1751.  
  1752. static void AddToOldEnvironment (const void *key, VISIT visit, int level)
  1753. {
  1754.     VariableList    *vp = *(VariableList **)key;
  1755.     VariableList    *vp1;
  1756.  
  1757.     if ((visit == postorder) || (visit == leaf))
  1758.     {
  1759.  
  1760. /* Skip local variables and traps */
  1761.  
  1762.     if ((ATOE_GFlags & FLAGS_FUNCTION) && (!(vp->status & STATUS_LOCAL)) &&
  1763.         (((*vp->name != CHAR_TILDE) || !vp->name[1])))
  1764.     {
  1765.  
  1766. /* Get the entry in the old variable list and update it with the new
  1767.  * parameters
  1768.  */
  1769.         vp1 = LookUpVariable (vp->name, vp->index, TRUE);
  1770.  
  1771.         if (vp1->value != null)
  1772.         ReleaseMemoryCell ((void *)vp1->value);
  1773.  
  1774.         vp1->value = vp->value;
  1775.         vp->value = null;        /* Stop releaseing this as its tx */
  1776.  
  1777.         vp1->status = vp->status;
  1778.         vp1->nvalue = vp->nvalue;
  1779.         vp1->base   = vp->base;
  1780.         vp1->width  = vp->width;
  1781.     }
  1782.     }
  1783. }
  1784.  
  1785. /*
  1786.  * Load GLobal Var List values
  1787.  */
  1788.  
  1789. static void F_LOCAL LoadGlobalVariableList (void)
  1790. {
  1791.     VariableList    *cifs = LookUpVariable (IFS, 0, TRUE);
  1792.  
  1793.     CurrentDirectory = LookUpVariable (tilde, 0, TRUE);
  1794.     RestoreCurrentDirectory (CurrentDirectory->value);
  1795.     SetCharacterTypes (cifs->value, C_IFS);
  1796. }
  1797.  
  1798. /*
  1799.  * Match a pattern as in sh(1).  Enhancement to handle prefix processing
  1800.  *
  1801.  * IgnoreCase - ignore case on comparisions.
  1802.  * end - end of match in 'string'.
  1803.  * mode - mode for match processing - see GM_ flags in sh.h
  1804.  *
  1805.  * pattern character are prefixed with MAGIC by expand.
  1806.  */
  1807.  
  1808. bool GeneralPatternMatch (char        *string,    /* String    */
  1809.               unsigned char *pattern,    /* Pattern    */
  1810.               bool        IgnoreCase,    /* Ignorecase    */
  1811.               char        **end,        /* End of match    */
  1812.               int        mode)        /* Mode        */
  1813. {
  1814.     int        string_c, pattern_c;
  1815.     char    *save_end;
  1816.  
  1817.     if ((string == (char *)NULL) || (pattern == (unsigned char *)NULL))
  1818.     return FALSE;
  1819.  
  1820.     while ((pattern_c = *(pattern++)) != 0)
  1821.     {
  1822.     string_c = *(string++);
  1823.  
  1824.     if (pattern_c != CHAR_MAGIC)
  1825.     {
  1826.         if (IgnoreCase)
  1827.         {
  1828.         string_c = tolower (string_c);
  1829.         pattern_c = tolower (pattern_c);
  1830.         }
  1831.  
  1832.         if (string_c != pattern_c)
  1833.         return FALSE;
  1834.  
  1835.         continue;
  1836.     }
  1837.  
  1838. /* Magic characters */
  1839.  
  1840.     switch (*(pattern++))
  1841.     {
  1842.         case CHAR_OPEN_BRACKETS:    /* Class expression        */
  1843.         if ((!string_c) ||
  1844.             ((pattern = CheckClassExpression (pattern, string_c,
  1845.                              IgnoreCase)) ==
  1846.                 (unsigned char *)NULL))
  1847.             return FALSE;
  1848.  
  1849.         break;
  1850.  
  1851.         case CHAR_MATCH_ANY:    /* Match any character        */
  1852.         if (string_c == 0)
  1853.             return FALSE;
  1854.  
  1855.         break;
  1856.  
  1857.         case CHAR_MATCH_ALL:    /* Match as many as possible    */
  1858.         --string;
  1859.         save_end = (char *)NULL;
  1860.  
  1861.         do
  1862.         {
  1863.             if (!*pattern ||
  1864.             GeneralPatternMatch (string, pattern, IgnoreCase, end,
  1865.                          mode))
  1866.             {
  1867.             if (mode == GM_LONGEST)
  1868.                 save_end = *end;
  1869.  
  1870.             else
  1871.                 return TRUE;
  1872.             }
  1873.  
  1874.         } while (*(string++));
  1875.  
  1876.         if (end != (char **)NULL)
  1877.             *end = save_end;
  1878.  
  1879.         return C2bool (save_end != (char *)NULL);
  1880.  
  1881.         default:        /* Match                */
  1882.         if ((unsigned)string_c != pattern[-1])
  1883.             return FALSE;
  1884.  
  1885.         break;
  1886.     }
  1887.     }
  1888.  
  1889.     if (end != (char **)NULL)
  1890.     {
  1891.     *end = string;
  1892.     return TRUE;
  1893.     }
  1894.  
  1895.     return C2bool (*string == 0);
  1896. }
  1897.  
  1898. /*
  1899.  * Process a class expression - []
  1900.  */
  1901.  
  1902. static unsigned char * F_LOCAL CheckClassExpression (
  1903.                 unsigned char    *pattern,
  1904.                 int        string_c, /* Match char*/
  1905.                 bool        IgnoreCase)/* Ic flag    */
  1906. {
  1907.     int        llimit_c, ulimit_c;
  1908.     bool    not = FALSE;
  1909.     bool    found;
  1910.  
  1911. /* Exclusive or inclusive class */
  1912.  
  1913.     if ((*pattern == CHAR_MAGIC) &&
  1914.     ((*(pattern + 1) == CHAR_NOT) || (*(pattern + 1) == '!')))
  1915.     {
  1916.     pattern += 2;
  1917.     not = TRUE;
  1918.     }
  1919.  
  1920.     found = not;
  1921.  
  1922. /* Process the pattern */
  1923.  
  1924.     do
  1925.     {
  1926.     if (*pattern == CHAR_MAGIC)
  1927.         pattern++;
  1928.  
  1929.     if (!*pattern)
  1930.         return (unsigned char *)NULL;
  1931.  
  1932. /* Get the next character in class, converting to lower case if necessary */
  1933.  
  1934.     llimit_c = IgnoreCase ? tolower (*pattern) : *pattern;
  1935.  
  1936. /* If this is a range, get the end of range character */
  1937.  
  1938.     if ((*(pattern + 1) == CHAR_MATCH_RANGE) &&
  1939.         (*(pattern + 2) != CHAR_CLOSE_BRACKETS))
  1940.     {
  1941.         ulimit_c = IgnoreCase ? tolower (*(pattern + 2)) : *(pattern + 2);
  1942.         pattern++;
  1943.     }
  1944.  
  1945.     else
  1946.         ulimit_c = llimit_c;
  1947.  
  1948. /* Is the current character in the class? */
  1949.  
  1950.     if ((llimit_c <= string_c) && (string_c <= ulimit_c))
  1951.         found = C2bool (!not);
  1952.  
  1953.     } while (*(++pattern) != CHAR_CLOSE_BRACKETS);
  1954.  
  1955.     return found ? pattern + 1 : (unsigned char *)NULL;
  1956. }
  1957.  
  1958. /*
  1959.  * Suffix processing - find the longest/shortest suffix.
  1960.  */
  1961.  
  1962. bool SuffixPatternMatch (char *string,    /* String to match        */
  1963.              char *pattern, /* Pattern to match against    */
  1964.              char **start,    /* Start position        */
  1965.              int  mode)    /* Match mode            */
  1966. {
  1967.     char    *save_start = (char *)NULL;
  1968.  
  1969. /* Scan the string, looking for a match to the end */
  1970.  
  1971.     while (*string)
  1972.     {
  1973.     if (GeneralPatternMatch (string, (unsigned char *)pattern, FALSE,
  1974.                  (char **)NULL, GM_ALL))
  1975.     {
  1976.  
  1977. /* If longest, stop here */
  1978.  
  1979.         if (mode == GM_LONGEST)
  1980.         {
  1981.         *start = string;
  1982.         return TRUE;
  1983.         }
  1984.  
  1985. /* Save the start of the shortest string so far and continue */
  1986.  
  1987.         save_start = string;
  1988.     }
  1989.  
  1990.     ++string;
  1991.     }
  1992.  
  1993.     return C2bool ((*start = save_start) != (char *)NULL);
  1994. }
  1995.  
  1996. /*
  1997.  * Get a string in a malloced area
  1998.  */
  1999.  
  2000. char *AllocateMemoryCell (size_t nbytes)
  2001. {
  2002.     s_region        *np;
  2003.     void        (*save_signal)(int);
  2004. #ifdef OS2_DOSALLOC
  2005.     SEL            sel;
  2006. #endif
  2007.  
  2008.     if (nbytes == 0)
  2009.     abort ();    /* silly and defeats the algorithm */
  2010.  
  2011. /* Grab some space */
  2012.  
  2013. #ifdef OS2_DOSALLOC
  2014.     if (DosAllocSeg (nbytes + sizeof (s_region), &sel, SEG_NONSHARED))
  2015.     {
  2016.     errno = ENOMEM;
  2017.     return (char *)NULL;
  2018.     }
  2019.  
  2020.     np = (s_region *)MAKEP (sel, 0);
  2021.     memset (np, 0, nbytes + sizeof (s_region));
  2022.  
  2023. #else
  2024.     if ((np = (s_region *)calloc (nbytes + sizeof (s_region), 1))
  2025.         == (s_region *)NULL)
  2026.     {
  2027.     errno = ENOMEM;
  2028.         return (char *)NULL;
  2029.     }
  2030. #endif
  2031.  
  2032. /* Disable signals */
  2033.  
  2034.     save_signal = signal (SIGINT, SIG_IGN);
  2035.  
  2036. /* Link into chain */
  2037.  
  2038.     np->next = MemoryAreaHeader;
  2039.     np->area = MemoryAreaLevel;
  2040.     np->nbytes = nbytes;
  2041.  
  2042.     MemoryAreaHeader = np;
  2043.  
  2044. /* Restore signals */
  2045.  
  2046.     signal (SIGINT, save_signal);
  2047.  
  2048.     return ((char *)np) + sizeof (s_region);
  2049. }
  2050.  
  2051. /*
  2052.  * Release a array of strings
  2053.  */
  2054.  
  2055. void    ReleaseAList (char **list)
  2056. {
  2057.     char    **ap = list;
  2058.  
  2059.     while (*ap != (char *)NULL)
  2060.     ReleaseMemoryCell (*(ap++));
  2061.  
  2062.     ReleaseMemoryCell (list);
  2063.  
  2064. }
  2065.  
  2066. /*
  2067.  * Free a string in a malloced area
  2068.  */
  2069.  
  2070. void ReleaseMemoryCell (void *s)
  2071. {
  2072.     s_region        *cp = MemoryAreaHeader;
  2073.     s_region        *lp = (s_region *)NULL;
  2074.     s_region        *sp = (s_region *)((char *)s - sizeof (s_region));
  2075.     void        (*save_signal)(int);
  2076.  
  2077. /* Disable signals */
  2078.  
  2079.     save_signal = signal (SIGINT, SIG_IGN);
  2080.  
  2081. /* Find the string in the chain */
  2082.  
  2083.     if (s != (char *)NULL)
  2084.     {
  2085.     while (cp != (s_region *)NULL)
  2086.     {
  2087.         if (cp != sp)
  2088.         {
  2089.         lp = cp;
  2090.         cp = cp->next;
  2091.         continue;
  2092.         }
  2093.  
  2094. /* First in chain ? */
  2095.  
  2096.         else if (lp == (s_region *)NULL)
  2097.         MemoryAreaHeader = cp->next;
  2098.  
  2099. /* Delete the current entry and relink */
  2100.  
  2101.         else
  2102.         lp->next = cp->next;
  2103.  
  2104.         RELEASE_MEMORY (cp);
  2105.         break;
  2106.     }
  2107.     }
  2108.  
  2109. /* Restore signals */
  2110.  
  2111.     signal (SIGINT, save_signal);
  2112. }
  2113.  
  2114. /*
  2115.  * Check for memory leaks with a dump
  2116.  */
  2117.  
  2118. #ifdef DEBUG_MEMORY
  2119. void DumpMemoryCells (int status)
  2120. {
  2121.     s_region        *cp = MemoryAreaHeader;
  2122.     size_t        i;
  2123.     char        buffer[17];
  2124.     char        *sp;
  2125.  
  2126. /* Find the string in the chain */
  2127.  
  2128.     while (cp != (s_region *)NULL)
  2129.     {
  2130.     fprintf (stderr, "Segment 0x%.8lx Area %5d Length %5d Link 0x%.8lx\n",
  2131.          cp, cp->area, cp->nbytes, cp->next);
  2132.  
  2133.     memset (buffer, CHAR_SPACE, 17);
  2134.     buffer[16] = 0;
  2135.  
  2136.     sp = ((char *)cp) + sizeof (s_region);
  2137.  
  2138.     for (i = 0; i < (((cp->nbytes - 1)/16) + 1) * 16; i++)
  2139.     {
  2140.         if (i >= cp->nbytes)
  2141.         {
  2142.         feputs ("   ");
  2143.         buffer [i % 16] = CHAR_SPACE;
  2144.         }
  2145.  
  2146.         else
  2147.         {
  2148.         fprintf (stderr, "%.2x ", *sp & 0x0ff);
  2149.         buffer [i % 16] = (char)(isprint (*sp) ? *sp : CHAR_PERIOD);
  2150.         }
  2151.  
  2152.         if (i % 16 == 15)
  2153.         fprintf (stderr, "    [%s]\n", buffer);
  2154.  
  2155.         sp++;
  2156.     }
  2157.  
  2158.     feputc (CHAR_NEW_LINE);
  2159.     cp = cp->next;
  2160.     }
  2161. #undef exit
  2162.     exit (status);
  2163. #define exit(x)        DumpMemoryCells (x)
  2164. }
  2165. #endif
  2166.  
  2167. /*
  2168.  * Autodelete space nolonger required.  Ie. Free all the strings in a malloced
  2169.  * area
  2170.  */
  2171.  
  2172. void ReleaseMemoryArea (int a)
  2173. {
  2174.     s_region        *cp = MemoryAreaHeader;
  2175.     s_region        *lp = (s_region *)NULL;
  2176.     void        (*save_signal)(int);
  2177.  
  2178. /* Release the Here documents first */
  2179.  
  2180.     FreeAllHereDocuments (a);
  2181.  
  2182. /* Disable signals */
  2183.  
  2184.     save_signal = signal (SIGINT, SIG_IGN);
  2185.  
  2186.     while (cp != (s_region *)NULL)
  2187.     {
  2188.  
  2189. /* Is the area number less than that specified - yes, continue */
  2190.  
  2191.     if (cp->area < a)
  2192.     {
  2193.         lp = cp;
  2194.         cp = cp->next;
  2195.     }
  2196.  
  2197. /* OK - delete the area.  Is it the first in chain ?  Yes, delete, relink
  2198.  * and update start location
  2199.  */
  2200.  
  2201.     else if (lp == (s_region *)NULL)
  2202.     {
  2203.         lp = cp;
  2204.         cp = cp->next;
  2205.         MemoryAreaHeader = cp;
  2206.  
  2207.         RELEASE_MEMORY (lp);
  2208.         lp = (s_region *)NULL;
  2209.     }
  2210.  
  2211. /* Not first, delete the current entry and relink */
  2212.  
  2213.     else
  2214.     {
  2215.         lp->next = cp->next;
  2216.         RELEASE_MEMORY (cp);
  2217.         cp = lp->next;
  2218.     }
  2219.     }
  2220.  
  2221. /* Restore signals */
  2222.  
  2223.     signal (SIGINT, save_signal);
  2224. }
  2225.  
  2226. /*
  2227.  * Set the area number for a malloced string.  This allows autodeletion of
  2228.  * space that is nolonger required.
  2229.  */
  2230.  
  2231. void SetMemoryAreaNumber (void *cp, int a)
  2232. {
  2233.     s_region    *sp = (s_region *)((char *)cp - sizeof (s_region));
  2234.  
  2235.     if (cp != (void *)NULL)
  2236.     sp->area = a;
  2237. }
  2238.  
  2239. /*
  2240.  * Get the area number for a malloced string
  2241.  */
  2242.  
  2243. int GetMemoryAreaNumber (void *cp)
  2244. {
  2245.     s_region    *sp = (s_region *)((char *)cp - sizeof (s_region));
  2246.  
  2247.     return sp->area;
  2248. }
  2249.  
  2250. /* Output one of the Prompt.  We save the prompt for the history part of
  2251.  * the program
  2252.  */
  2253.  
  2254. void OutputUserPrompt (char *s)
  2255. {
  2256.     struct tm        *tm;
  2257.     time_t        xtime = time ((time_t *)NULL);
  2258.     int            i;
  2259.     char        buf[PATH_MAX + 4];
  2260.  
  2261.     if (LastUserPrompt != (char *)NULL)
  2262.     {
  2263.     LastUserPrompt = s;        /* Save the Last prompt id    */
  2264.     s = GetVariableAsString (s, TRUE); /* Get the string value    */
  2265.  
  2266.     if (LastUserPrompt == PS1)
  2267.         s = substitute (s, 0);
  2268.     }
  2269.  
  2270.     else
  2271.     s = LastUserPrompt1;
  2272.  
  2273.     tm = localtime (&xtime);
  2274.  
  2275.     while (*s)
  2276.     {
  2277.  
  2278. /* If a format character, process it */
  2279.  
  2280.     if (*s == CHAR_FORMAT)
  2281.     {
  2282.         s++;
  2283.         *s = (char)tolower(*s);
  2284.  
  2285.         if (*s == CHAR_FORMAT)
  2286.         fputchar (CHAR_FORMAT);
  2287.  
  2288.         else
  2289.         {
  2290.         *buf = 0;
  2291.  
  2292.         switch (*(s++))
  2293.         {
  2294.             case 'e':            /* Current event number */
  2295.             if (HistoryEnabled)
  2296.                 sprintf (buf, "%d", Current_Event + 1);
  2297.  
  2298.             break;
  2299.  
  2300.             case 't':            /* time        */
  2301.             sprintf (buf,"%.2d:%.2d", tm->tm_hour, tm->tm_min);
  2302.             break;
  2303.  
  2304.             case 'd':            /* date        */
  2305.             sprintf (buf, "%.3s %.2d-%.2d-%.2d",
  2306.                  &"SunMonTueWedThuFriSat"[tm->tm_wday * 3],
  2307.                  tm->tm_mday, tm->tm_mon + 1,
  2308.                  tm->tm_year % 100);
  2309.             break;
  2310.  
  2311.             case 'p':            /* directory    */
  2312.             case 'n':            /* default drive */
  2313.             strcpy (buf, CurrentDirectory->value);
  2314.  
  2315.             if (*(s - 1) == 'n')
  2316.                 buf[1] = 0;
  2317.  
  2318.             break;
  2319.  
  2320. #if (OS_TYPE != OS_UNIX)
  2321.             case 'v':            /* version        */
  2322.             sprintf (buf, "%s %.2d:%.2d", LIT_OSname,
  2323.                 OS_VERS_N, _osminor);
  2324.             break;
  2325. #endif
  2326.         }
  2327.  
  2328. /* Output the string */
  2329.  
  2330.         foputs (buf);
  2331.         }
  2332.     }
  2333.  
  2334. /* Escaped character ? */
  2335.  
  2336.     else if (*s == CHAR_META)
  2337.     {
  2338.         ++s;
  2339.         if ((i = ProcessOutputMetaCharacters (&s)) == -1)
  2340.         i = 0;
  2341.  
  2342.         fputchar ((char)i);
  2343.     }
  2344.  
  2345.     else
  2346.         fputchar (*(s++));
  2347.     }
  2348.  
  2349.     FlushStreams ();            /* Clear output */
  2350. }
  2351.  
  2352. /*
  2353.  * Get the current path in UNIX format and save it in the environment
  2354.  * variable $~
  2355.  */
  2356.  
  2357. void GetCurrentDirectoryPath (void)
  2358. {
  2359.     char    ldir[PATH_MAX + 6];
  2360.     char    *CurrentPWDValue;        /* Current directory    */
  2361.  
  2362.     S_getcwd (ldir, 0);
  2363.  
  2364. /* Save in environment */
  2365.  
  2366.     SetVariableFromString (tilde, ldir);
  2367.     CurrentDirectory = LookUpVariable (tilde, 0, TRUE);
  2368.  
  2369. /* If we have changed directory, set PWD and OLDPWD */
  2370.  
  2371.     if (strcmp (CurrentPWDValue = GetVariableAsString (PWDVariable, FALSE),
  2372.         ldir))
  2373.     {
  2374.     SetVariableFromString (OldPWDVariable, CurrentPWDValue);
  2375.     SetVariableFromString (PWDVariable, ldir);
  2376.     }
  2377. }
  2378.  
  2379. /*
  2380.  * Initialise the shell and Patch up various parts of the system for the
  2381.  * shell.  At the moment, we modify the ctype table so that _ is an upper
  2382.  * case character.
  2383.  */
  2384.  
  2385. static bool F_LOCAL Initialise (int argc, char **argv)
  2386. {
  2387.     char    *s, *s1;
  2388.     char    **ap;
  2389.     char    *name = *argv;
  2390.     bool    OptionsRflag = FALSE;
  2391. #if (OS_TYPE == OS_NT)
  2392.     bool    Level0 = FALSE;
  2393.     int        sc;
  2394. #endif
  2395.  
  2396. /*
  2397.  * Determine the base OS if we can!
  2398.  */
  2399.  
  2400. #if (OS_TYPE == OS_NT)
  2401.     BaseOS = BASE_OS_NT;    /* Default to NT - no other */
  2402. #elif (OS_TYPE == OS_UNIX)
  2403.     BaseOS = BASE_OS_UNIX;    /* Default to UNIX - no other */
  2404. #elif (OS_TYPE == OS_OS2)
  2405.     BaseOS = BASE_OS_OS2;    /* Default to OS2 - no other */
  2406. #elif (OS_TYPE == OS_DOS)
  2407.  
  2408.     BaseOS = BASE_OS_DOS;    /* Default to DOS */
  2409.  
  2410.     if (_osmajor > 10)        /* Rather crude check */
  2411.     BaseOS = BASE_OS_OS2;
  2412.  
  2413. /* Know to stop NT ? */
  2414.  
  2415.     else
  2416.     {
  2417.         union REGS    r;
  2418.     r.h.ah = 0x16;
  2419.     r.h.al = 0;
  2420.  
  2421.     SystemInterrupt (0x2f, &r, &r);
  2422.  
  2423.     if (r.h.al > 0)
  2424.         BaseOS = BASE_OS_WIN;
  2425.     
  2426.     else
  2427.     {
  2428.         r.x.REG_AX = 0x3306;
  2429.         SystemInterrupt (0x21, &r, &r);
  2430.  
  2431.         if ((r.x.REG_AX == 0x3306) && (r.x.REG_BX == 0x3205))
  2432.         BaseOS = BASE_OS_NT;
  2433.     }
  2434.     }
  2435. #endif
  2436.  
  2437. /*
  2438.  * For MSDOS: Get original interrupt 24 address and set up our new interrupt
  2439.  *          24 address.
  2440.  * For OS/2 : Set noignore interrupts.
  2441.  */
  2442.  
  2443. #if (OS_TYPE == OS_DOS)  && !defined (__EMX__)
  2444.     Orig_I24_V = GetInterruptVector (0x24);
  2445. #  if (OS_SIZE == OS_16)
  2446.     SetInterruptVector (0x24, SW_Int24);
  2447. #  else
  2448.     _harderr (HardErrorHandler);
  2449. #  endif
  2450. #elif (OS_TYPE != OS_DOS)
  2451.     IgnoreInterrupts = FALSE;
  2452. #endif
  2453.  
  2454. /* Create terminations queues - OS/2 only */
  2455.  
  2456.     CreateTerminationQueues ();
  2457.  
  2458. /* Set up character maps */
  2459.  
  2460.     InitialiseCharacterTypes ();
  2461.  
  2462. /* Create the integer variables, in case they are loaded from the
  2463.  * environment
  2464.  */
  2465.     CreateIntegerVariables ();
  2466.  
  2467. /* Initialise Path Extension lists */
  2468.  
  2469.     BuildExtensionLists ();
  2470.  
  2471. /* For NT, we need to know now if this is a level 0 shell because NT has a
  2472.  * nasty habit of being case in-sensitive!  Not only for file names, but
  2473.  * also environment variables.  So scan the command line for the -0 option!
  2474.  */
  2475.  
  2476. #if (OS_TYPE == OS_NT)
  2477.     while ((sc = GetOptions (argc, argv, ShellOptions, 0)) != EOF)
  2478.     {
  2479.     if (sc == '0')
  2480.     {
  2481.         Level0 = TRUE;
  2482.         break;
  2483.     }
  2484.     }
  2485.  
  2486.     ResetGetOptions ();
  2487. #endif
  2488.  
  2489. /* Load the environment into our structures */
  2490.  
  2491.     if ((ap = environ) != NOWORDS)
  2492.     {
  2493.     for (ap = environ; *ap != (char *)NULL; ap++)
  2494.     {
  2495.  
  2496. /* Set up any variables.  Note there is an assumption that
  2497.  * AssignVariableFromString sets the equals sign to 0, hiding the value;
  2498.  */
  2499.         if (!strncmp ("SECONDS=", *ap, 8))
  2500.         continue;
  2501.  
  2502. /* NT is case in-sensitive - what a PAIN! */
  2503.  
  2504. #if (OS_TYPE == OS_NT)
  2505.         if (Level0)
  2506.         {
  2507.         s = *ap;
  2508.         
  2509.         while (*s && (*s != CHAR_ASSIGN))
  2510.         {
  2511.             *s = toupper (*s);
  2512.             s++;
  2513.         }
  2514.         }
  2515. #endif
  2516.         if (AssignVariableFromString (*ap, (int *)NULL))
  2517.         SetVariableStatus (*ap, STATUS_EXPORT);
  2518.     }
  2519.     }
  2520.  
  2521. /* Change COMSPEC to unix format for execution */
  2522.  
  2523.     PATH_TO_UNIX (GetVariableAsString (ComspecVariable, FALSE));
  2524.     SetVariableStatus (ComspecVariable, STATUS_CONVERT_MSDOS);
  2525.  
  2526. /* Zap all files */
  2527.  
  2528.     CloseAllHandlers ();
  2529.     MemoryAreaLevel = 1;
  2530.  
  2531. /* Get the current directory */
  2532.  
  2533.     GetCurrentDirectoryPath ();
  2534.  
  2535. /* Initialise the getopts command */
  2536.  
  2537.     ResetGetoptsValues (TRUE);
  2538.  
  2539. /* Set up SHELL variable.  First check for a restricted shell.  Check the
  2540.  * restricted shell
  2541.  */
  2542.  
  2543.     SetVariableFromString (LastWordVariable, name);
  2544.  
  2545. /* For OS/2, we prefer the full path name and not that in argv[0].  Save a
  2546.  * copy of the string
  2547.  */
  2548.  
  2549. #if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_NT)
  2550.     PATH_TO_UNIX ((name = GetAllocatedSpace (strlen (_APgmName) + 4)));
  2551.     SetMemoryAreaNumber ((void *)name, 0);
  2552.     strcpy (name, _APgmName);
  2553. #endif
  2554.  
  2555. /* Has the program name got a .exe extension - Yes probably DOS 3+.  So
  2556.  * save it as the Shell name.  Under OS/2, make sure we use .exe 
  2557.  */
  2558.  
  2559.     if (GetVariableAsString (ShellVariableName, FALSE) == null)
  2560.     {
  2561. #if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_NT)
  2562.     if ((s1 = strrchr (name, CHAR_PERIOD)) == (char *)NULL)
  2563.         strcat (name, EXEExtension);
  2564.  
  2565.     SetVariableFromString (ShellVariableName, name);
  2566.  
  2567.     ReleaseMemoryCell (name);
  2568. #elif (OS_TYPE == OS_DOS)
  2569.     if (((s1 = strrchr (name, CHAR_PERIOD)) != (char *)NULL) &&
  2570.         (stricmp (s1 + 1, EXEExtension + 1) == 0))
  2571.         SetVariableFromString (ShellVariableName, PATH_TO_UNIX (name));
  2572. #elif (OS_TYPE == OS_UNIX)
  2573.     SetVariableFromString (ShellVariableName,
  2574.                 (*name == '-') ? name + 1 : name);
  2575. #endif
  2576.     }
  2577.  
  2578. /* Default if necessary */
  2579.  
  2580.     if (GetVariableAsString (ShellVariableName, FALSE) == null)
  2581.     SetVariableFromString (ShellVariableName, shellname);
  2582.  
  2583.     PATH_TO_UNIX (s1 = GetVariableAsString (ShellVariableName, FALSE));
  2584.  
  2585. /* Check for restricted shell */
  2586.  
  2587.     if ((s = FindLastPathCharacter (s1)) == (char *)NULL)
  2588.     s = s1;
  2589.  
  2590.     else
  2591.     s++;
  2592.  
  2593.     if (*s == 'r')
  2594.     OptionsRflag = TRUE;
  2595.  
  2596. /* Set up home directory */
  2597.  
  2598.     if (GetVariableAsString (HomeVariableName, FALSE) == null)
  2599.     {
  2600.         if ((s = GetVariableAsString ("INIT", FALSE)) == null)
  2601.             s = CurrentDirectory->value;
  2602.  
  2603.     SetVariableFromString (HomeVariableName, s);
  2604.     }
  2605.  
  2606. /* Set up OS Mode */
  2607.  
  2608. #if defined (__TURBOC__) 
  2609.     SetVariableFromNumeric (LIT_OSmode, 0L);
  2610. #elif (__WATCOMC__) 
  2611.     SetVariableFromNumeric (LIT_OSmode, (long)OS_TYPE - 1);
  2612. #elif (OS_TYPE == OS_UNIX)
  2613.     SetVariableFromNumeric (LIT_OSmode, 3L);
  2614. #else
  2615.     SetVariableFromNumeric (LIT_OSmode, (long)_osmode);
  2616. #endif
  2617.  
  2618. #if (OS_SIZE == OS_32)
  2619.     SetVariableFromNumeric (LIT_SHmode, 32L);
  2620. #else
  2621.     SetVariableFromNumeric (LIT_SHmode, 16L);
  2622. #endif
  2623.  
  2624. /* Set up history file location */
  2625.  
  2626.     SetVariableFromNumeric (LIT_Dollar, (long)getpid ());
  2627.  
  2628.     LoadGlobalVariableList ();
  2629.     PATH_TO_UNIX (GetVariableAsString (PathLiteral, FALSE));
  2630.     PATH_TO_UNIX (GetVariableAsString (CDPathLiteral, FALSE));
  2631.  
  2632.     return OptionsRflag;
  2633. }
  2634.  
  2635. /*
  2636.  * Mail Check processing.  Every $MAILCHECK seconds, we check either $MAIL
  2637.  * or $MAILPATH to see if any file has changed its modification time since
  2638.  * we last looked.  In $MAILCHECK, the files are separated by semi-colon (;).
  2639.  * If the filename contains a %, the string following the % is the message
  2640.  * to display if the file has changed.
  2641.  */
  2642.  
  2643. static void F_LOCAL CheckForMailArriving (void)
  2644. {
  2645.     int            delay = (int)GetVariableAsNumeric (MailCheckVariable);
  2646.     char        *mail = GetVariableAsString ("MAIL", FALSE);
  2647.     char        *mailp = GetVariableAsString ("MAILPATH", FALSE);
  2648.     static time_t    last = 0L;
  2649.     time_t        current = time ((time_t *)NULL);
  2650.     struct stat        st;
  2651.     char        *cp, *sp, *ap;
  2652.  
  2653. /* Have we waited long enough */
  2654.  
  2655.     if (((current - last) < delay) || (DisabledVariables & DISABLE_MAILCHECK))
  2656.     return;
  2657.  
  2658. /* Yes - Check $MAILPATH.  If it is defined, process it.  Otherwise, use
  2659.  * $MAIL
  2660.  */
  2661.  
  2662.     if (mailp != null)
  2663.     {
  2664.  
  2665. /* Check MAILPATH */
  2666.  
  2667.     sp = mailp;
  2668.  
  2669. /* Look for the next separator */
  2670.  
  2671.     while ((cp = strchr (sp, CHAR_PATH_SEPARATOR)) != (char *)NULL)
  2672.     {
  2673.         *cp = 0;
  2674.  
  2675. /* % in string ? */
  2676.  
  2677.         if ((ap = strchr (sp, CHAR_FORMAT)) != (char *)NULL)
  2678.         *ap = 0;
  2679.  
  2680. /* Check the file name */
  2681.  
  2682.         if ((S_stat (sp, &st)) && (st.st_mtime > last) && st.st_size)
  2683.         {
  2684.         feputs ((ap != (char *)NULL) ? ap + 1 : ymail);
  2685.         feputc (CHAR_NEW_LINE);
  2686.         }
  2687.  
  2688. /* Restore the % */
  2689.  
  2690.         if (ap != (char *)NULL)
  2691.         *ap = CHAR_FORMAT;
  2692.  
  2693. /* Restore the semi-colon and find the next one */
  2694.  
  2695.         *cp = CHAR_PATH_SEPARATOR;
  2696.         sp = cp + 1;
  2697.     }
  2698.     }
  2699.  
  2700. /* Just check MAIL */
  2701.  
  2702.     else if ((mail != null) && (S_stat (mail, &st)) &&
  2703.          (st.st_mtime > last) && st.st_size)
  2704.     {
  2705.     feputs (ymail);
  2706.     feputc (CHAR_NEW_LINE);
  2707.     }
  2708.  
  2709. /* Save the last check time */
  2710.  
  2711.     last = current;
  2712. }
  2713.  
  2714. /*
  2715.  * Preprocess Argv to get handle of options in the format /x
  2716.  *
  2717.  * Some programs invoke the shell using / instead of - to mark the options.
  2718.  * We need to convert to -.  Also /c is a special case.  The rest of the
  2719.  * command line is the command to execute.  So, we get the command line
  2720.  * from the original buffer instead of argv array.
  2721.  */
  2722.  
  2723. #if (OS_TYPE != OS_UNIX)
  2724. static void F_LOCAL Pre_Process_Argv (char **argv, int *argc1)
  2725. {
  2726.     int        argc = 1;
  2727.     char    *ocl = _ACmdLine;
  2728.     int        i;
  2729.  
  2730. /* Check for these options */
  2731.  
  2732.     while ((*++argv != (char *)NULL) && (strlen (*argv) == 2) &&
  2733.        (**argv == '/'))
  2734.     {
  2735.     argc++;
  2736.     *strlwr (*argv) = CHAR_SWITCH;
  2737.  
  2738. /* Get the original information from the command line */
  2739.  
  2740.     if ((*argv)[1] == 'c')
  2741.     {
  2742.  
  2743. /*
  2744.  * In some case under OS/2, we get /c, but with EMX style.  So all we need
  2745.  * to do is change the / to a - and return.
  2746.  */
  2747.  
  2748. #  if (OS_TYPE == OS_OS2)
  2749.         if (EMXStyleParameters)
  2750.             return;
  2751. #  endif
  2752.  
  2753. /*
  2754.  * Otherwise, parse the command line again, looking for the /c
  2755.  */
  2756.  
  2757.         while ((*ocl != '/') && (*(ocl + 1) != 'c') &&
  2758.            (*ocl) && (*ocl != CHAR_RETURN))
  2759.         ++ocl;
  2760.  
  2761.         if (*ocl != '/')
  2762.         continue;
  2763.  
  2764. /* Find the start of the string */
  2765.  
  2766.         ocl += 2;
  2767.  
  2768.         while (isspace (*ocl) && (*ocl != CHAR_RETURN))
  2769.         ++ocl;
  2770.  
  2771.         if (*ocl == CHAR_RETURN)
  2772.         continue;
  2773.  
  2774. /* Found the start.  Set up next parameter and ignore the rest */
  2775.  
  2776.         if (*(argv + 1) == (char *)NULL)
  2777.         continue;
  2778.  
  2779.         argc++;
  2780.  
  2781. /* Remove quotes from string, if they are there */
  2782.  
  2783.         if ((*ocl == CHAR_DOUBLE_QUOTE) &&
  2784.         (ocl[i = (strlen (ocl) - 1)] == CHAR_DOUBLE_QUOTE))
  2785.         {
  2786.         ocl[i] = 0;
  2787.         ocl++;
  2788.         }
  2789.  
  2790. /* Set up new argument array */
  2791.  
  2792.         *(argv + 1) = ocl;
  2793.         *(argv + 2) = (char *)NULL;
  2794.         *argc1 = argc;
  2795.  
  2796.         if ((ocl = strchr (ocl, CHAR_RETURN)) != (char *)NULL)
  2797.         *ocl = 0;
  2798.  
  2799.         return;
  2800.     }
  2801.     }
  2802. }
  2803. #endif
  2804.  
  2805. /*
  2806.  * Convert path format to/from UNIX
  2807.  */
  2808.  
  2809. char *ConvertPathToFormat (char *path, char in, char out)
  2810. {
  2811. #if (OS_TYPE == OS_UNIX)
  2812.     return path;
  2813. #else
  2814.     char    *s = path;
  2815.  
  2816.     while ((path = strchr (path, in)) != (char *)NULL)
  2817.     *path = out;
  2818.  
  2819.     return s;
  2820. #endif
  2821. }
  2822.  
  2823. /* Load profiles onto I/O Stack */
  2824.  
  2825. static void F_LOCAL LoadTheProfileFiles (void)
  2826. {
  2827.     char    *name;
  2828.     char    *Pname;
  2829.  
  2830.     if ((Pname = GetVariableAsString ("ETCPROFILE", FALSE)) == null)
  2831.     {
  2832.     Pname = "x:/etc/profile";
  2833.     *Pname = GetDriveLetter (GetRootDiskDrive ());
  2834.     }
  2835.  
  2836.     InteractiveFlag = TRUE;
  2837.     ExecuteShellScript (Pname);
  2838.  
  2839. /*
  2840.  * Check $HOME format.  If in DOS format, mark and convert to UNIX
  2841.  */
  2842.  
  2843.     if (strchr (name = GetVariableAsString (HomeVariableName, FALSE),
  2844.         CHAR_DOS_PATH) != (char *)NULL)
  2845.     {
  2846.     PATH_TO_UNIX (name);
  2847.     SetVariableStatus (HomeVariableName, STATUS_CONVERT_MSDOS);
  2848.     }
  2849.  
  2850.     name = BuildFileName ("profile");     /* Set up home profile */
  2851.     ExecuteShellScript (name);
  2852.     ReleaseMemoryCell ((void *)name);
  2853. }
  2854.  
  2855. /*
  2856.  * Convert Unix PATH to MSDOS PATH
  2857.  */
  2858.  
  2859. static void F_LOCAL ConvertUnixPathToMSDOS (char *cp)
  2860. {
  2861.     char    *scp = cp;
  2862.     int        colon = 0;
  2863.  
  2864. /* If there is a semi-colon or a backslash, we assume this is a DOS format
  2865.  * path
  2866.  */
  2867.  
  2868.     if ((strchr (cp, CHAR_PATH_SEPARATOR) != (char *)NULL) ||
  2869.     (strchr (cp, CHAR_DOS_PATH) != (char *)NULL))
  2870.     return;
  2871.  
  2872. /* Count the number of colons */
  2873.  
  2874.     while ((cp = strchr (cp, CHAR_COLON)) != (char *)NULL)
  2875.     {
  2876.     ++colon;
  2877.     ++cp;
  2878.     }
  2879.  
  2880. /* If there are no colons or there is one colon as the second character, it
  2881.  * is probably an MSDOS path
  2882.  */
  2883.  
  2884.     cp = scp;
  2885.     if ((colon == 0) || ((colon == 1) && (*(cp + 1) == CHAR_COLON)))
  2886.     return;
  2887.  
  2888. /* Otherwise, convert all colons to semis */
  2889.  
  2890.     while ((cp = strchr (cp, CHAR_COLON)) != (char *)NULL)
  2891.     *(cp++) = CHAR_PATH_SEPARATOR;
  2892. }
  2893.  
  2894. /* Generate a file name from a directory and name.  Return null if an error
  2895.  * occurs or some malloced space containing the file name otherwise
  2896.  */
  2897.  
  2898. char *BuildFileName (char *name)
  2899. {
  2900.     char    *dir = GetVariableAsString (HomeVariableName, FALSE);
  2901.     char    *cp;
  2902.  
  2903. /* Get some space */
  2904.  
  2905.     if ((cp = AllocateMemoryCell (strlen (dir) + strlen (name) + 2))
  2906.         == (char *)NULL)
  2907.     return null;
  2908.  
  2909. /* Addend the directory and a / if the directory does not end in one */
  2910.  
  2911.     strcpy (cp, dir);
  2912.  
  2913.     if (!IsPathCharacter (cp[strlen (cp) - 1]))
  2914.     strcat (cp, DirectorySeparator);
  2915.  
  2916. /* Append the file name */
  2917.  
  2918.     return strcat (cp, name);
  2919. }
  2920.  
  2921. /* Clear prompts */
  2922.  
  2923. static void F_LOCAL ClearUserPrompts (void)
  2924. {
  2925.     ClearVariableStatus (PS1, STATUS_EXPORT);
  2926.     ClearVariableStatus (PS2, STATUS_EXPORT);
  2927.     ClearVariableStatus (PS3, STATUS_EXPORT);
  2928.     SetVariableFromString (PS1, null);
  2929.     SetVariableFromString (PS2, null);
  2930.     SetVariableFromString (PS3, null);
  2931. }
  2932.  
  2933. /* Process setting of SECONDS and RANDOM environment variables */
  2934.  
  2935. static void F_LOCAL SecondAndRandomEV (char *name, long val)
  2936. {
  2937.     if (!strcmp (name, SecondsVariable) &&
  2938.     !(DisabledVariables & DISABLE_SECONDS))
  2939.     ShellStartTime = time ((time_t *)NULL) - val;
  2940.  
  2941.     else if (!strcmp (name, RandomVariable) &&
  2942.          !(DisabledVariables & DISABLE_RANDOM))
  2943.     srand ((int)val);
  2944. }
  2945.  
  2946. /*
  2947.  * Set up the Window name.  Give up if it does not work.
  2948.  */
  2949.  
  2950. #if (OS_TYPE == OS_OS2)
  2951. void    SetWindowName (char *title)
  2952. {
  2953.     HSWITCH        hswitch;
  2954.     SWCNTRL        swctl;
  2955.     char        *cp;
  2956.  
  2957.     if ((!(hswitch = WinQuerySwitchHandle (0, getpid ()))) ||
  2958.     (WinQuerySwitchEntry (hswitch, &swctl)))
  2959.     return;
  2960.  
  2961.     if (title != (char *)NULL)
  2962.         cp = title;
  2963.  
  2964.     else if ((DisabledVariables & DISABLE_WINTITLE) ||
  2965.          ((cp = GetVariableAsString (WinTitleVariable, FALSE)) == null))
  2966.     cp = DefaultWinTitle;
  2967.  
  2968.     strncpy (swctl.szSwtitle, cp, sizeof (swctl.szSwtitle));
  2969.     swctl.szSwtitle[sizeof (swctl.szSwtitle) - 1] = 0;
  2970.     WinChangeSwitchEntry (hswitch, &swctl);
  2971. }
  2972. #endif
  2973.  
  2974. /* NT Version */
  2975.  
  2976. #if (OS_TYPE == OS_NT)
  2977. void    SetWindowName (char *title)
  2978. {
  2979.     char        *cp;
  2980.  
  2981.     if (title != (char *)NULL)
  2982.         cp = title;
  2983.  
  2984.     else if ((DisabledVariables & DISABLE_WINTITLE) ||
  2985.          ((cp = GetVariableAsString (WinTitleVariable, FALSE)) == null))
  2986.     cp = DefaultWinTitle;
  2987.  
  2988.     SetConsoleTitle (cp);
  2989. }
  2990. #endif
  2991.  
  2992. /*
  2993.  * In OS/2, check for terminated processes
  2994.  */
  2995.  
  2996. #if (OS_TYPE == OS_OS2)
  2997. static void F_LOCAL CheckForTerminatedProcess (void)
  2998. {
  2999.     RESULTCODES        rescResults;
  3000.     PID            pidProcess;
  3001.     char        *s;
  3002.     OSCALL_RET        rc;
  3003.  
  3004. #  if (OS_TYPE == OS_OS2)
  3005.    CheckForSessionEnd ();
  3006. #  endif
  3007.  
  3008. /* Check for tasks terminating */
  3009.  
  3010.     while (TRUE)
  3011.     {
  3012.     while ((rc = DosCwait (DCWA_PROCESSTREE, DCWW_NOWAIT, &rescResults,
  3013.                    &pidProcess, 0)) == ERROR_INTERRUPT)
  3014.         continue;
  3015.  
  3016. /* Ignore errors */
  3017.  
  3018.     if (rc)
  3019.         return;
  3020.  
  3021. /* Remove the job */
  3022.  
  3023.     DeleteJob (pidProcess);
  3024.  
  3025.     switch (rescResults.codeTerminate)
  3026.     {
  3027.         case TC_EXIT:
  3028.         s = "Normal Exit";
  3029.         break;
  3030.  
  3031.         case TC_HARDERROR:
  3032.         s = "Hard error";
  3033.         break;
  3034.  
  3035.         case TC_TRAP:
  3036.         s = "Trapped";
  3037.         break;
  3038.  
  3039.         case TC_KILLPROCESS:
  3040.         s = "Killed";
  3041.         break;
  3042.  
  3043.         default:
  3044.         s = "Unknown";
  3045.         break;
  3046.  
  3047.     }
  3048.  
  3049.     fprintf (stderr, "Process %d terminated - %s (%d)\n", pidProcess, s,
  3050.          rescResults.codeTerminate);
  3051.     }
  3052. }
  3053. #endif
  3054.  
  3055. /* NT Version */
  3056.  
  3057. #if (OS_TYPE == OS_NT)
  3058. static void F_LOCAL CheckForTerminatedProcess (void)
  3059. {
  3060.     twalk (JobTree, LookUpJobs);
  3061. }
  3062.  
  3063. /*
  3064.  * Walk the NT job tree looking for terminated processes
  3065.  */
  3066.  
  3067. static void LookUpJobs (const void *key, VISIT visit, int level)
  3068. {
  3069.     JobList    *job = *(JobList **)key;
  3070.     HANDLE    hp;
  3071.     DWORD    res;
  3072.     DWORD    ExitCode;
  3073.  
  3074.     if ((visit == postorder) || (visit == leaf))
  3075.     {
  3076.     if ((hp = OpenProcess (PROCESS_ALL_ACCESS, (BOOL)TRUE,
  3077.                    job->pid)) == NULL)
  3078.     {
  3079.         PrintWarningMessage ("sh: Cannot open process %d\n%s", job->pid,
  3080.                  GetOSSystemErrorMessage (GetLastError ()));
  3081.         DeleteJob (job->pid);
  3082.     }
  3083.  
  3084. /* Wait for the object to exit */
  3085.  
  3086.     else if ((res = WaitForSingleObject (hp, 0)) == WAIT_OBJECT_0)
  3087.     {
  3088.         DeleteJob (job->pid);
  3089.  
  3090.         if (!GetExitCodeProcess (hp, &ExitCode))
  3091.         PrintWarningMessage (
  3092.             "sh: Cannot get termination code for process %d\n%s",
  3093.             job->pid, GetOSSystemErrorMessage (GetLastError ()));
  3094.         else
  3095.         fprintf (stderr, "Process %d terminated (%ld)\n", job->pid,
  3096.              ExitCode);
  3097.     }
  3098.  
  3099. /* Failed?  - error */
  3100.  
  3101.     else if (res == WAIT_FAILED)
  3102.     {
  3103.         PrintWarningMessage ("sh: Cannot wait for process %d\n%s",
  3104.                  job->pid,
  3105.                  GetOSSystemErrorMessage (GetLastError ()));
  3106.  
  3107.         DeleteJob (job->pid);
  3108.     }
  3109.  
  3110.     CloseHandle (hp);
  3111.     }
  3112. }
  3113. #endif
  3114.  
  3115. /* UNIX version */
  3116.  
  3117. #if (OS_TYPE == OS_UNIX)
  3118. static void F_LOCAL CheckForTerminatedProcess (void)
  3119. {
  3120.     fputs ("UNIX: CheckForTerminatedProcess NI\n", stderr);
  3121. }
  3122. #endif
  3123.  
  3124.  
  3125. /*
  3126.  * Check for end of a Session
  3127.  */
  3128.  
  3129. #if (OS_TYPE == OS_OS2)
  3130. static void F_LOCAL    CheckForSessionEnd (void)
  3131. {
  3132.     OSCALL_PARAM    DataLength;
  3133.     OSCALL_RET        rc;
  3134.     BYTE        bElemPriority;
  3135.     struct SessionEnd
  3136.     {
  3137.         unsigned short    SessionId;
  3138.     unsigned short    ExitCode;
  3139.     }            *DataAddress;
  3140.  
  3141. #  if (OS_SIZE == OS_32)
  3142.     REQUESTDATA        Request;
  3143. #  else
  3144.     QUEUERESULT        Request;
  3145. #  endif
  3146.  
  3147. /* Check for sessions terminating */
  3148.  
  3149.     while ((rc = DosReadQueue (SessionQueueHandler, &Request, &DataLength,
  3150.                               (PVOID *)&DataAddress,
  3151.                               0, DCWW_NOWAIT, &bElemPriority,
  3152.                    SessionQueueSema)) == NO_ERROR)
  3153.     {
  3154.     DeleteJobBySession (DataAddress->SessionId);
  3155.  
  3156.     fprintf (stderr, "Session %d terminated - Normal Exit (%d)\n",
  3157.          DataAddress->SessionId, DataAddress->ExitCode);
  3158.  
  3159. #  if (OS_SIZE == OS_32)
  3160.     DosFreeMem (DataAddress);
  3161. #  else
  3162.     DosFreeSeg (SELECTOROF ((DataAddress)));
  3163. #  endif
  3164.     }
  3165. }
  3166. #endif
  3167.  
  3168. /*
  3169.  * Set up the Parameter Environment variables
  3170.  */
  3171.  
  3172. static void F_LOCAL SetUpParameterEV (int argc, char **argv, char *name)
  3173. {
  3174.     Word_B    *wb = (Word_B *)NULL;
  3175.     char    *Value;
  3176.     int        i;
  3177.  
  3178.     if ((Value = StringSave (name)) == null)
  3179.     {
  3180.     fprintf (stderr, BasicErrorMessage, ShellNameLiteral, Outofmemory1);
  3181.     return;
  3182.     }
  3183.  
  3184.     wb = AddParameter (Value, wb, ShellNameLiteral);
  3185.  
  3186.     for (i = 1; i < argc; ++i)
  3187.     {
  3188.     if ((!AssignVariableFromString (argv[i], (int *)NULL)) &&
  3189.         (wb != (Word_B *)NULL))
  3190.     {
  3191.         if ((Value = StringSave (argv[i])) != null)
  3192.         wb = AddParameter (Value, wb, ShellNameLiteral);
  3193.  
  3194.         else
  3195.         {
  3196.         fprintf (stderr, BasicErrorMessage, ShellNameLiteral,
  3197.              Outofmemory1);
  3198.         return;
  3199.         }
  3200.     }
  3201.     }
  3202.  
  3203.     if (wb != (Word_B *)NULL)
  3204.     wb = AddParameter ((char *)NULL, wb, ShellNameLiteral);
  3205. }
  3206.  
  3207. /*
  3208.  * Update the Seconds and Random variables
  3209.  */
  3210.  
  3211. void HandleSECONDandRANDOM (void)
  3212. {
  3213.     if (!(DisabledVariables & DISABLE_SECONDS))
  3214.     LookUpVariable (SecondsVariable, 0, TRUE);
  3215.  
  3216.     if (!(DisabledVariables & DISABLE_RANDOM))
  3217.     LookUpVariable (RandomVariable, 0, TRUE);
  3218. }
  3219.  
  3220. /*
  3221.  * Set the status of an environment variable
  3222.  */
  3223.  
  3224. void SetVariableStatus (char *name,        /* Variable name    */
  3225.             int  flag)        /* New status        */
  3226. {
  3227.     VariableList    *vp = LookUpVariable (name, 0, TRUE);
  3228.  
  3229.     if (IS_VariableFC ((int)*vp->name))    /* not an internal symbol ($# etc) */
  3230.     vp->status |= flag;
  3231. }
  3232.  
  3233. /*
  3234.  * Array version - only 0 is exported
  3235.  */
  3236.  
  3237. void SetVariableArrayStatus (char *name,    /* Variable name    */
  3238.                  int  index,    /* Array index        */
  3239.                  int  flag)        /* New status        */
  3240. {
  3241.     VariableList    *vp = LookUpVariable (name, index, TRUE);
  3242.  
  3243.     if (IS_VariableFC ((int)*vp->name))    /* not an internal symbol ($# etc) */
  3244.     {
  3245.     vp->status |= flag;
  3246.  
  3247.     if (index)
  3248.         vp->status &= ~STATUS_EXPORT;
  3249.     }
  3250. }
  3251.  
  3252. /*
  3253.  * Set the status of an environment variable
  3254.  */
  3255.  
  3256. void ClearVariableStatus (char *name, int flag)
  3257. {
  3258.     VariableList    *vp = LookUpVariable (name, 0, TRUE);
  3259.  
  3260.     if (IS_VariableFC ((int)*vp->name))    /* not an internal symbol ($# etc) */
  3261.     vp->status &= ~flag;
  3262. }
  3263.  
  3264. /*
  3265.  * Check allowed to set variable
  3266.  */
  3267.  
  3268. static bool F_LOCAL AllowedToSetVariable (VariableList *vp)
  3269. {
  3270.     if (vp->status & STATUS_READONLY)
  3271.     {
  3272.     ShellErrorMessage (LIT_2Strings, vp->name, LIT_IsReadonly);
  3273.     return FALSE;
  3274.     }
  3275.  
  3276. /* Check for $PATH, $SHELL or $ENV reset in restricted shell */
  3277.  
  3278.     if ((!strcmp (vp->name, PathLiteral) || !strcmp (vp->name, ENVVariable) ||
  3279.      !strcmp (vp->name, ShellVariableName)) &&
  3280.     CheckForRestrictedShell (PathLiteral))
  3281.     return FALSE;
  3282.  
  3283.     return TRUE;
  3284. }
  3285.  
  3286. /*
  3287.  * Set up a variable from a string
  3288.  */
  3289.  
  3290. void SetVariableFromString (char *name, char *val)
  3291. {
  3292.     SetVariableArrayFromString (name, 0, val);
  3293. }
  3294.  
  3295. /*
  3296.  * Array index version
  3297.  */
  3298.  
  3299. void SetVariableArrayFromString (char *name, int Index, char *val)
  3300. {
  3301.     VariableList    *vp = LookUpVariable (name, Index, TRUE);
  3302.     char        *xp = null;
  3303.     long        nval;
  3304.  
  3305. /* Check if allowed to set variable */
  3306.  
  3307.     if (!AllowedToSetVariable (vp))
  3308.     return;
  3309.  
  3310. /* If we change the PATH to a new value, we need to untrack some aliases */
  3311.  
  3312.     if (!strcmp (name, PathLiteral) && strcmp (vp->value, val))
  3313.     UnTrackAllAliases ();
  3314.  
  3315.     CheckOPTIND (name, atol (val));
  3316.  
  3317. /* Save the new value */
  3318.  
  3319.     if ((!(vp->status & STATUS_INTEGER)) && (val != null) && strlen (val) &&
  3320.     ((xp = StringSave (val = SuppressSpacesZeros (vp, val))) == null))
  3321.         return;
  3322.  
  3323. /* Free the old value if appropriate */
  3324.  
  3325.     if (vp->value != null)
  3326.     ReleaseMemoryCell ((void *)vp->value);
  3327.  
  3328.     vp->value = null;
  3329.  
  3330. /* String value? */
  3331.  
  3332.     if (!(vp->status & STATUS_INTEGER))
  3333.     {
  3334.     vp->value = xp;
  3335.  
  3336.     if (!vp->width)
  3337.         vp->width = strlen (val);
  3338.     }
  3339.  
  3340. /* No - Number value */
  3341.  
  3342.     else if (!ValidMathsExpression (val, &nval))
  3343.     {
  3344.     SecondAndRandomEV (name, nval);
  3345.     SetUpANumericValue (vp, nval, -1);
  3346.     }
  3347.     
  3348.  
  3349. /* Check to see if it should be exported */
  3350.  
  3351.     if (FL_TEST (FLAG_ALL_EXPORT))
  3352.     vp->status |= STATUS_EXPORT;
  3353.  
  3354. /* Convert UNIX to DOS for PATH variable */
  3355.  
  3356.     if (!strcmp (name, PathLiteral))
  3357.     ConvertUnixPathToMSDOS (GetVariableAsString (PathLiteral, FALSE));
  3358.  
  3359.     else if (!strcmp (name, CDPathLiteral))
  3360.     ConvertUnixPathToMSDOS (GetVariableAsString (CDPathLiteral, FALSE));
  3361.  
  3362.     else if (!strcmp (name, PathExtsLiteral))
  3363.     BuildExtensionLists ();
  3364.  
  3365. /* Set up IFS characters */
  3366.  
  3367.     else if (!strcmp (name, IFS))
  3368.     SetCharacterTypes (val, C_IFS);
  3369.  
  3370. /* Check for title change */
  3371.  
  3372. #if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
  3373.     else if (!strcmp (name, FCEditVariable))
  3374.     SetEditorMode (val);
  3375.  
  3376.     else if (!strcmp (name, EditorVariable))
  3377.     SetEditorMode (val);
  3378.  
  3379.     else if (!strcmp (name, VisualVariable))
  3380.     SetEditorMode (val);
  3381. #endif
  3382.  
  3383. #if (OS_TYPE != OS_DOS)
  3384.     else if ((!(DisabledVariables & DISABLE_WINTITLE)) &&
  3385.          (!strcmp (name, WinTitleVariable)))
  3386.     SetWindowName ((char *)NULL);
  3387. #endif
  3388. }
  3389.  
  3390. /*
  3391.  * Set a variable from a numeric
  3392.  */
  3393.  
  3394. void        SetVariableFromNumeric (char *name, long value)
  3395. {
  3396.     SetVariableArrayFromNumeric (name, 0, value);
  3397. }
  3398.  
  3399. /* Array version */
  3400.  
  3401. void        SetVariableArrayFromNumeric (char *name, int Index, long value)
  3402. {
  3403.     VariableList    *vp = LookUpVariable (name, Index, TRUE);
  3404.     char        NumericBuffer[20];
  3405.  
  3406. /* Check if allowed to set variable */
  3407.  
  3408.     if (!AllowedToSetVariable (vp))
  3409.     return;
  3410.  
  3411.     CheckOPTIND (name, value);
  3412.  
  3413.     if (!(vp->status & STATUS_INTEGER))
  3414.     {
  3415.     sprintf (NumericBuffer, "%ld", value);
  3416.     SetVariableFromString (name, NumericBuffer);
  3417.     }
  3418.  
  3419. /* Save the integer value */
  3420.  
  3421.     else
  3422.     SetUpANumericValue (vp, value, -1);
  3423.  
  3424.     SecondAndRandomEV (name, value);
  3425. }
  3426.  
  3427. /*
  3428.  * Get variable as a numeric
  3429.  */
  3430.  
  3431. long        GetVariableAsNumeric (char *name)
  3432. {
  3433.     return GetVariableArrayAsNumeric (name, 0);
  3434. }
  3435.  
  3436. /* Array version */
  3437.  
  3438. long        GetVariableArrayAsNumeric (char *name, int Index)
  3439. {
  3440.     VariableList    *vp = LookUpVariable (name, Index, FALSE);
  3441.  
  3442.     if (vp->status & STATUS_INTEGER)
  3443.     return vp->nvalue;
  3444.  
  3445.     else
  3446.     return atol (vp->value);
  3447. }
  3448.  
  3449. /*
  3450.  * Get variable as a formatted string
  3451.  */
  3452.  
  3453. char        *GetVariableAsString (char *name, bool Format)
  3454. {
  3455.     return GetVariableArrayAsString (name, 0, Format);
  3456. }
  3457.  
  3458. /*
  3459.  * Indexed version
  3460.  */
  3461.  
  3462. char        *GetVariableArrayAsString (char *name, int Index, bool Format)
  3463. {
  3464.     VariableList    *vp = LookUpVariable (name, Index, FALSE);
  3465.     char        *Value = vp->value;
  3466.     char        *xp;
  3467.     size_t        len;
  3468.     char        *NumericBuffer;
  3469.  
  3470.     if (vp->status & STATUS_INTEGER)
  3471.     {
  3472.     if ((NumericBuffer = GetAllocatedSpace (40)) == (char *)NULL)
  3473.         return null;
  3474.  
  3475.     if (vp->base != 10)
  3476.     {
  3477.         sprintf (NumericBuffer, LIT_BNumber, vp->base);
  3478.         xp = NumericBuffer + strlen (NumericBuffer);
  3479.     }
  3480.  
  3481.     else
  3482.         xp = NumericBuffer;
  3483.  
  3484.         ltoa (vp->nvalue, xp, vp->base);
  3485.     return NumericBuffer;
  3486.     }
  3487.  
  3488. /* Handle a string variable, if no formating required, return it */
  3489.  
  3490.     if (!Format)
  3491.     return vp->value;
  3492.  
  3493. /* Left justify ? */
  3494.  
  3495.     if (vp->status & STATUS_LEFT_JUSTIFY)
  3496.     {
  3497.     xp = SuppressSpacesZeros (vp, Value);
  3498.  
  3499.     if ((Value = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
  3500.         return null;
  3501.  
  3502.     memset (Value, CHAR_SPACE, vp->width);
  3503.     Value[vp->width] = 0;
  3504.  
  3505.     if ((len = strlen (xp)) > vp->width)
  3506.         len = vp->width;
  3507.  
  3508.     memcpy (Value, xp, len);
  3509.     }
  3510.  
  3511. /* Right justify ? */
  3512.  
  3513.     else if (vp->status & (STATUS_RIGHT_JUSTIFY | STATUS_ZERO_FILL))
  3514.     {
  3515.     if ((xp = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
  3516.         return null;
  3517.  
  3518.     if ((len = strlen (Value)) < vp->width)
  3519.     {
  3520.         memset (xp, ((vp->status & STATUS_ZERO_FILL) &&
  3521.              (isdigit (*Value))) ? '0' : CHAR_SPACE, vp->width);
  3522.  
  3523.         memcpy (xp + (vp->width - len), Value, len);
  3524.     }
  3525.  
  3526.     else
  3527.         memcpy (xp, Value + vp->width - len, vp->width);
  3528.  
  3529.     *(xp + vp->width) = 0;
  3530.     Value = xp;
  3531.     }
  3532.  
  3533. /* Handle upper and lower case conversions */
  3534.  
  3535.     if (vp->status & STATUS_LOWER_CASE)
  3536.     Value = strlwr (StringCopy (Value));
  3537.  
  3538.     if (vp->status & STATUS_UPPER_CASE)
  3539.     Value = strupr (StringCopy (Value));
  3540.  
  3541.     return Value;
  3542. }
  3543.  
  3544. /*
  3545.  * Set up a numeric value
  3546.  */
  3547.  
  3548. static void F_LOCAL SetUpANumericValue (VariableList *vp, long value, int base)
  3549. {
  3550.     vp->nvalue = value;
  3551.     vp->status |= STATUS_INTEGER;
  3552.  
  3553.     if (vp->base == 0)
  3554.     vp->base = (base > 1) ? base
  3555.                   : ((LastNumberBase != -1) ? LastNumberBase
  3556.                             : 10);
  3557.  
  3558.     if (vp->value != null)
  3559.     ReleaseMemoryCell ((void *)vp->value);
  3560.  
  3561.     vp->value = null;
  3562. }
  3563.  
  3564. /*
  3565.  * Suppress leading spaces and zeros
  3566.  */
  3567.  
  3568. static char * F_LOCAL SuppressSpacesZeros (VariableList *vp, char *value)
  3569. {
  3570.     if (vp->status & STATUS_LEFT_JUSTIFY)
  3571.     {
  3572.     while (*value == CHAR_SPACE)
  3573.         value++;
  3574.  
  3575.     if (vp->status & STATUS_ZERO_FILL)
  3576.     {
  3577.         while (*value == '0')
  3578.         value++;
  3579.     }
  3580.     }
  3581.  
  3582.     return value;
  3583. }
  3584.  
  3585. /*
  3586.  * Check to see if a reset of CheckOPTIND has occured
  3587.  */
  3588.  
  3589. static void F_LOCAL CheckOPTIND (char *name, long value)
  3590. {
  3591.     if ((value == 1) && (!(DisabledVariables & DISABLE_OPTIND)) &&
  3592.     (strcmp (OptIndVariable, name) == 0))
  3593.     ResetGetoptsValues (FALSE);
  3594. }
  3595.  
  3596. /*
  3597.  * Initialise the Integer variables by creating them
  3598.  */
  3599.  
  3600. static void F_LOCAL CreateIntegerVariables (void)
  3601. {
  3602.     struct ShellVariablesInit    *wp = InitialiseShellVariables;
  3603.  
  3604.     while  (wp->Name != (char *)NULL)
  3605.     {
  3606.     SetVariableStatus (wp->Name, wp->Status);
  3607.  
  3608.     if (wp->CValue != null)
  3609.         SetVariableFromString (wp->Name, wp->CValue);
  3610.  
  3611.     wp++;
  3612.     }
  3613. }
  3614.  
  3615. /*
  3616.  * Close up and exit
  3617.  */
  3618.  
  3619. void    FinalExitCleanUp (int status)
  3620. {
  3621. #if (OS_TYPE == OS_OS)
  3622. #  if (OS_SIZE == OS_32)
  3623.     DosCloseEventSem (SessionQueueSema);
  3624.     DosCloseQueue (SessionQueueHandler);
  3625. #  else
  3626.     DosCloseSem (SessionQueueSema);
  3627.     DosCloseQueue (SessionQueueHandler);
  3628. #  endif
  3629. #endif
  3630.  
  3631.     exit (status);
  3632. }
  3633.  
  3634. /*
  3635.  * Create Session termination semaphores and queues.  Also get the number
  3636.  * file handlers.
  3637.  */
  3638.  
  3639. #if (OS_TYPE == OS_OS2)
  3640. static void F_LOCAL CreateTerminationQueues (void)
  3641. {
  3642.     int            count = 0;
  3643.     static char        Name[25];
  3644.     OSCALL_RET        rc;
  3645. #  if (OS_SIZE == OS_32)
  3646.     LONG        ReqCount = 0;        /* Increment        */
  3647.     ULONG        CurMaxFH;        /* Available File handlers */
  3648.  
  3649.     DosSetRelMaxFH (&ReqCount, &CurMaxFH);
  3650.     MaxNumberofFDs = min (CurMaxFH, 32 + FDBASE);
  3651. #  endif
  3652.  
  3653. /* Create semaphore for queue */
  3654.  
  3655.     while (TRUE)
  3656.     {
  3657. #  if (OS_SIZE == OS_32)
  3658.         sprintf (Name, "\\SEM32\\SHELL\\%.5d", count++);
  3659. #  else
  3660.         sprintf (Name, "\\SEM\\SHELL\\%.5d", count++);
  3661. #  endif
  3662.  
  3663. #  if (OS_SIZE == OS_32)
  3664.     if ((rc = DosCreateEventSem (Name, &SessionQueueSema,
  3665.                      DC_SEM_SHARED, TRUE)) == NO_ERROR)
  3666.         break;
  3667. #  else
  3668.     if ((rc = DosCreateSem (CSEM_PUBLIC, &SessionQueueSema,
  3669.                 Name)) == NO_ERROR)
  3670.     {
  3671.         DosSemClear (SessionQueueSema);
  3672.         break;
  3673.     }
  3674. #  endif
  3675.  
  3676. /* Check for error */
  3677.  
  3678. #  if (OS_SIZE == OS_32)
  3679.     if (rc != ERROR_DUPLICATE_NAME)
  3680. #  else
  3681.     if (rc != ERROR_ALREADY_EXISTS)
  3682. #  endif
  3683.     {
  3684.         SessionQueueSema = 0;
  3685.         PrintErrorMessage ("DosCreateSem: Cannot create semaphore\n%s",
  3686.                        GetOSSystemErrorMessage (rc));
  3687.     }
  3688.     }
  3689.  
  3690. /* Create the queue */
  3691.  
  3692.     count = 0;
  3693.  
  3694.     while (TRUE)
  3695.     {
  3696. #  if (OS_SIZE == OS_32)
  3697.         sprintf (Name, "\\QUEUES\\SHELL\\%.5d", count++);
  3698. #  else
  3699.         sprintf (Name, "\\QUEUES\\SHQ%.5d", count++);
  3700. #  endif
  3701.  
  3702.     if ((rc = DosCreateQueue (&SessionQueueHandler,
  3703. #ifdef QUE_CONVERT_ADDRESS
  3704.                   QUE_FIFO | QUE_CONVERT_ADDRESS,
  3705. #else
  3706.                   QUE_FIFO,
  3707. #endif
  3708.  
  3709.                   Name)) == NO_ERROR)
  3710.         break;
  3711.  
  3712. /* Check for error */
  3713.  
  3714.     if (rc != ERROR_QUE_DUPLICATE)
  3715.     {
  3716.         SessionQueueHandler = 0;
  3717.         PrintErrorMessage ("DosCreateQueue: Cannot create queue\n%s",
  3718.                        GetOSSystemErrorMessage (rc));
  3719.     }
  3720.     }
  3721.  
  3722.     SessionEndQName = Name;
  3723. }
  3724. #endif
  3725.  
  3726. /*
  3727.  * Set up Edit mode
  3728.  */
  3729.  
  3730. #if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
  3731. static void F_LOCAL SetEditorMode (char *ed)
  3732. {
  3733.     char    *rcp;
  3734.  
  3735.     if ((rcp = strrchr (ed, '/')) != (char *)NULL)
  3736.     ed = rcp + 1;
  3737.  
  3738. #  ifdef FLAGS_EMACS
  3739.     if (strstr (ed, "emacs"))
  3740.     {
  3741.     ShellGlobalFlags &= ~FLAGS_EDITORS;
  3742.     ShellGlobalFlags |= FLAGS_EMACS;
  3743.     }
  3744. #  endif
  3745.  
  3746. #  ifdef FLAGS_VI
  3747.     if (strstr (ed, "vi"))
  3748.     {
  3749.     ShellGlobalFlags &= ~FLAGS_EDITORS;
  3750.     ShellGlobalFlags |= FLAGS_VI;
  3751.     }
  3752. #  endif
  3753.  
  3754. #  ifdef FLAGS_GMACS
  3755.     if (strstr (ed, "gmacs"))
  3756.     {
  3757.     ShellGlobalFlags &= ~FLAGS_EDITORS;
  3758.     ShellGlobalFlags |= FLAGS_GMACS;
  3759.     }
  3760. #  endif
  3761. }
  3762. #endif
  3763.